<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 1.5.8">
<title>docToolchain Manual</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
<style>
/* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */
/* Uncomment @import statement below to use as custom stylesheet */
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}
audio,canvas,video{display:inline-block}
audio:not([controls]){display:none;height:0}
script{display:none!important}
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
a{background:transparent}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
abbr[title]{border-bottom:1px dotted}
b,strong{font-weight:bold}
dfn{font-style:italic}
hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,*::before,*::after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.center{margin-left:auto;margin-right:auto}
.stretch{width:100%}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:none}
p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
ul.square{list-style-type:square}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}
abbr{text-transform:none}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}
blockquote cite::before{content:"\2014 \0020"}
blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}
table thead,table tfoot{background:#f7f8f7}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table}
.clearfix::after,.float-group::after{clear:both}
*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed;word-wrap:break-word}
*:not(pre)>code.nobreak{word-wrap:normal}
*:not(pre)>code.nowrap{white-space:nowrap}
pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed}
em em{font-style:normal}
strong strong{font-weight:400}
.keyseq{color:rgba(51,51,51,.8)}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menuref{color:#000}
.menuseq b:not(.caret),.menuref{font-weight:inherit}
.menuseq{word-spacing:-.02em}
.menuseq b.caret{font-size:1.25em;line-height:.8}
.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}
b.button::before,b.button::after{position:relative;top:-1px;font-weight:400}
b.button::before{content:"[";padding:0 3px 0 2px}
b.button::after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table}
#header::after,#content::after,#footnotes::after,#footer::after{clear:both}
#content{margin-top:1.25em}
#content::before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span::before{content:"\00a0\2013\00a0"}
#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark::before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber::after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
#toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}}
@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:rgba(255,255,255,.8);line-height:1.44}
#content{margin-bottom:.625em}
.sect1{padding-bottom:.625em}
@media screen and (min-width:768px){#content{margin-bottom:1.25em}
.sect1{padding-bottom:1.25em}}
.sect1:last-child{padding-bottom:0}
.sect1+.sect1{border-top:1px solid #e7e7e9}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
.paragraph.lead>p,#preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
table.tableblock #preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:inherit}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6)}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8}
.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}
.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;overflow-x:auto;padding:1em;font-size:.8125em}
@media screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}
@media screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}
.literalblock pre.nowrap,.literalblock pre.nowrap pre,.listingblock pre.nowrap,.listingblock pre.nowrap pre{white-space:pre;word-wrap:normal}
.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.listingblock>.content{position:relative}
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999}
.listingblock:hover code[data-lang]::before{display:block}
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:#999}
.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"}
table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0;line-height:1.45}
table.pyhltable td.code{padding-left:.75em;padding-right:0}
pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #dddddf}
pre.pygments .lineno{display:inline-block;margin-right:.25em}
table.pyhltable .linenodiv{background:none!important;padding-right:0!important}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}
.verseblock{margin:0 1em 1.25em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}
.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}
.quoteblock.abstract{margin:0 1em 1.25em;display:block}
.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}
.quoteblock.excerpt,.quoteblock .quoteblock{margin:0 0 1.25em;padding:0 0 .25em 1em;border-left:.25em solid #dddddf}
.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}
.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;text-align:left;margin-right:0}
table.tableblock{max-width:100%;border-collapse:separate}
p.tableblock:last-child{margin-bottom:0}
td.tableblock>.content{margin-bottom:-1.25em}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all>thead>tr>.tableblock,table.grid-all>tbody>tr>.tableblock{border-width:0 1px 1px 0}
table.grid-all>tfoot>tr>.tableblock{border-width:1px 1px 0 0}
table.grid-cols>*>tr>.tableblock{border-width:0 1px 0 0}
table.grid-rows>thead>tr>.tableblock,table.grid-rows>tbody>tr>.tableblock{border-width:0 0 1px}
table.grid-rows>tfoot>tr>.tableblock{border-width:1px 0 0}
table.grid-all>*>tr>.tableblock:last-child,table.grid-cols>*>tr>.tableblock:last-child{border-right-width:0}
table.grid-all>tbody>tr:last-child>.tableblock,table.grid-all>thead:last-child>tr>.tableblock,table.grid-rows>tbody>tr:last-child>.tableblock,table.grid-rows>thead:last-child>tr>.tableblock{border-bottom-width:0}
table.frame-all{border-width:1px}
table.frame-sides{border-width:0 1px}
table.frame-topbot,table.frame-ends{border-width:1px 0}
table.stripes-all tr,table.stripes-odd tr:nth-of-type(odd){background:#f8f8f7}
table.stripes-none tr,table.stripes-odd tr:nth-of-type(even){background:none}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
td>div.verse{white-space:pre}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
ul.unstyled,ol.unstyled{margin-left:0}
ul.checklist{margin-left:.625em}
ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}
ul.inline{display:-ms-flexbox;display:-webkit-box;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
ul.inline>li{margin-left:1.25em}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}
.colist td:not([class]):first-child img{max-width:none}
.colist td:not([class]):last-child{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}
.imageblock.left{margin:.25em .625em 1.25em 0}
.imageblock.right{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
.gist .file-data>table td.line-data{width:99%}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background-color:#00fafa}
.black{color:#000}
.black-background{background-color:#000}
.blue{color:#0000bf}
.blue-background{background-color:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background-color:#fa00fa}
.gray{color:#606060}
.gray-background{background-color:#7d7d7d}
.green{color:#006000}
.green-background{background-color:#007d00}
.lime{color:#00bf00}
.lime-background{background-color:#00fa00}
.maroon{color:#600000}
.maroon-background{background-color:#7d0000}
.navy{color:#000060}
.navy-background{background-color:#00007d}
.olive{color:#606000}
.olive-background{background-color:#7d7d00}
.purple{color:#600060}
.purple-background{background-color:#7d007d}
.red{color:#bf0000}
.red-background{background-color:#fa0000}
.silver{color:#909090}
.silver-background{background-color:#bcbcbc}
.teal{color:#006060}
.teal-background{background-color:#007d7d}
.white{color:#bfbfbf}
.white-background{background-color:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background-color:#fafa00}
span.icon>.fa{cursor:default}
a span.icon>.fa{cursor:inherit}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]::after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@page{margin:1.25cm .75cm}
@media print{*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}
html{font-size:80%}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]::after{content:" (" attr(title) ")"}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span::before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]::before{display:block}
#footer{padding:0 .9375em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
@media print,amzn-kf8{#header>h1:first-child{margin-top:1.25rem}
.sect1{padding:0!important}
.sect1+.sect1{border:0}
#footer{background:none}
#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}
@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<style>
/* Stylesheet for CodeRay to match GitHub theme | MIT License | http://foundation.zurb.com */
/*pre.CodeRay {background-color:#f7f7f8;}*/
.CodeRay .line-numbers{border-right:1px solid #d8d8d8;padding:0 0.5em 0 .25em}
.CodeRay span.line-numbers{display:inline-block;margin-right:.5em;color:rgba(0,0,0,.3)}
.CodeRay .line-numbers strong{color:rgba(0,0,0,.4)}
table.CodeRay{border-collapse:separate;border-spacing:0;margin-bottom:0;border:0;background:none}
table.CodeRay td{vertical-align: top;line-height:1.45}
table.CodeRay td.line-numbers{text-align:right}
table.CodeRay td.line-numbers>pre{padding:0;color:rgba(0,0,0,.3)}
table.CodeRay td.code{padding:0 0 0 .5em}
table.CodeRay td.code>pre{padding:0}
.CodeRay .debug{color:#fff !important;background:#000080 !important}
.CodeRay .annotation{color:#007}
.CodeRay .attribute-name{color:#000080}
.CodeRay .attribute-value{color:#700}
.CodeRay .binary{color:#509}
.CodeRay .comment{color:#998;font-style:italic}
.CodeRay .char{color:#04d}
.CodeRay .char .content{color:#04d}
.CodeRay .char .delimiter{color:#039}
.CodeRay .class{color:#458;font-weight:bold}
.CodeRay .complex{color:#a08}
.CodeRay .constant,.CodeRay .predefined-constant{color:#008080}
.CodeRay .color{color:#099}
.CodeRay .class-variable{color:#369}
.CodeRay .decorator{color:#b0b}
.CodeRay .definition{color:#099}
.CodeRay .delimiter{color:#000}
.CodeRay .doc{color:#970}
.CodeRay .doctype{color:#34b}
.CodeRay .doc-string{color:#d42}
.CodeRay .escape{color:#666}
.CodeRay .entity{color:#800}
.CodeRay .error{color:#808}
.CodeRay .exception{color:inherit}
.CodeRay .filename{color:#099}
.CodeRay .function{color:#900;font-weight:bold}
.CodeRay .global-variable{color:#008080}
.CodeRay .hex{color:#058}
.CodeRay .integer,.CodeRay .float{color:#099}
.CodeRay .include{color:#555}
.CodeRay .inline{color:#000}
.CodeRay .inline .inline{background:#ccc}
.CodeRay .inline .inline .inline{background:#bbb}
.CodeRay .inline .inline-delimiter{color:#d14}
.CodeRay .inline-delimiter{color:#d14}
.CodeRay .important{color:#555;font-weight:bold}
.CodeRay .interpreted{color:#b2b}
.CodeRay .instance-variable{color:#008080}
.CodeRay .label{color:#970}
.CodeRay .local-variable{color:#963}
.CodeRay .octal{color:#40e}
.CodeRay .predefined{color:#369}
.CodeRay .preprocessor{color:#579}
.CodeRay .pseudo-class{color:#555}
.CodeRay .directive{font-weight:bold}
.CodeRay .type{font-weight:bold}
.CodeRay .predefined-type{color:inherit}
.CodeRay .reserved,.CodeRay .keyword {color:#000;font-weight:bold}
.CodeRay .key{color:#808}
.CodeRay .key .delimiter{color:#606}
.CodeRay .key .char{color:#80f}
.CodeRay .value{color:#088}
.CodeRay .regexp .delimiter{color:#808}
.CodeRay .regexp .content{color:#808}
.CodeRay .regexp .modifier{color:#808}
.CodeRay .regexp .char{color:#d14}
.CodeRay .regexp .function{color:#404;font-weight:bold}
.CodeRay .string{color:#d20}
.CodeRay .string .string .string{background:#ffd0d0}
.CodeRay .string .content{color:#d14}
.CodeRay .string .char{color:#d14}
.CodeRay .string .delimiter{color:#d14}
.CodeRay .shell{color:#d14}
.CodeRay .shell .delimiter{color:#d14}
.CodeRay .symbol{color:#990073}
.CodeRay .symbol .content{color:#a60}
.CodeRay .symbol .delimiter{color:#630}
.CodeRay .tag{color:#008080}
.CodeRay .tag-special{color:#d70}
.CodeRay .variable{color:#036}
.CodeRay .insert{background:#afa}
.CodeRay .delete{background:#faa}
.CodeRay .change{color:#aaf;background:#007}
.CodeRay .head{color:#f8f;background:#505}
.CodeRay .insert .insert{color:#080}
.CodeRay .delete .delete{color:#800}
.CodeRay .change .change{color:#66f}
.CodeRay .head .head{color:#f4f}
</style>
</head>
<body class="article toc2 toc-left">
<div id="header">
<h1>docToolchain Manual</h1>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#_introduction_and_goals">1. Introduction and Goals</a>
<ul class="sectlevel2">
<li><a href="#_create_awesome_docs">1.1. Create awesome docs!</a></li>
<li><a href="#_benefits_of_the_docs_as_code_approach">1.2. Benefits of the docs-as-code Approach</a></li>
</ul>
</li>
<li><a href="#_how_to_install_doctoolchain">2. How to install docToolchain</a>
<ul class="sectlevel2">
<li><a href="#_get_the_tool">2.1. Get the tool</a></li>
<li><a href="#_initialize_directory_for_documents">2.2. Initialize directory for documents</a></li>
<li><a href="#_build">2.3. Build</a></li>
<li><a href="#_publish_to_confluence">2.4. Publish to Confluence</a></li>
</ul>
</li>
<li><a href="#_overview_of_available_tasks">3. Overview of available Tasks</a>
<ul class="sectlevel2">
<li><a href="#_conventions">3.1. Conventions</a></li>
<li><a href="#_generatehtml">3.2. generateHTML</a></li>
<li><a href="#_fixencoding">3.3. fixEncoding</a></li>
<li><a href="#_prependfilename">3.4. prependFilename</a></li>
<li><a href="#_collectincludes">3.5. collectIncludes</a></li>
<li><a href="#_generatepdf">3.6. generatePDF</a></li>
<li><a href="#_generatedocbook">3.7. generateDocbook</a></li>
<li><a href="#_generatedeck">3.8. generateDeck</a></li>
<li><a href="#_publishtoconfluence">3.9. publishToConfluence</a></li>
<li><a href="#_converttodocx">3.10. convertToDocx</a></li>
<li><a href="#_createreferencedoc">3.11. createReferenceDoc</a></li>
<li><a href="#_converttoepub">3.12. convertToEpub</a></li>
<li><a href="#_exportea">3.13. exportEA</a></li>
<li><a href="#_exportvisio">3.14. exportVisio</a></li>
<li><a href="#_exportdrawio">3.15. exportDrawIo</a></li>
<li><a href="#_exportchangelog">3.16. exportChangeLog</a></li>
<li><a href="#_exportcontributors">3.17. exportContributors</a></li>
<li><a href="#_exportjiraissues">3.18. exportJiraIssues</a></li>
<li><a href="#_exportppt">3.19. exportPPT</a></li>
<li><a href="#_exportexcel">3.20. exportExcel</a></li>
<li><a href="#_exportmarkdown">3.21. exportMarkdown</a></li>
<li><a href="#_htmlsanitycheck">3.22. htmlSanityCheck</a></li>
<li><a href="#_dependencyupdates">3.23. dependencyUpdates</a></li>
</ul>
</li>
<li><a href="#_development">4. Development</a>
<ul class="sectlevel2">
<li><a href="#_how_to_run_tests">4.1. How to run Tests</a></li>
</ul>
</li>
<li><a href="#_faq_frequently_asked_questions">5. FAQ: Frequently asked Questions</a>
<ul class="sectlevel2">
<li><a href="#_images">5.1. Images</a></li>
<li><a href="#_exportvisio_2">5.2. exportVisio</a></li>
<li><a href="#_sparx_enterprise_architect">5.3. Sparx Enterprise Architect</a></li>
<li><a href="#_known_error_messages">5.4. Known error Messages</a></li>
</ul>
</li>
<li><a href="#_further_reading">6. Further Reading</a>
<ul class="sectlevel2">
<li><a href="#_links">6.1. Links</a></li>
<li><a href="#_books">6.2. Books</a></li>
<li><a href="#_past_and_upcoming_talks">6.3. Past and upcoming Talks</a></li>
</ul>
</li>
<li><a href="#_acknowledgements_and_contributors">7. Acknowledgements and Contributors</a></li>
<li><a href="#_configuration_3">Appendix A: Configuration</a>
<ul class="sectlevel2">
<li><a href="#_mainconfigfile_and_docdir">A.1. <code>mainConfigFile</code> and <code>docDir</code></a></li>
<li><a href="#_asciidoc_config">A.2. AsciiDoc config</a></li>
<li><a href="#_command_line_parameters">A.3. Command Line Parameters</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<style>
.gravatar img {
    margin-left: 3px;
    border-radius: 4px;
}
</style>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect1">
<h2 id="_introduction_and_goals">1. Introduction and Goals</h2>
<div class="sectionbody">
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/01_introduction_and_goals.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/01_introduction_and_goals.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5f528a3f6814d28b583f31842e3e8d9e?d=identicon" alt="Alexander Schwartz " width="32" title="Alexander Schwartz "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/34ef1437fd31ff96c31dff58125e93fe?d=identicon" alt="Dr. Stefan Pfeiffer " width="32" title="Dr. Stefan Pfeiffer "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/69124393772349d32fb9fe975545757d?d=identicon" alt="Lars Francke " width="32" title="Lars Francke "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/3ab6e54862dd8caaa33e9984edd5e550?d=identicon" alt="Michael Müller " width="32" title="Michael Müller "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>5 minutes to read</pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Jump directly to the <a href="https://github.com/docToolchain/docToolchain">github repository</a>
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="_create_awesome_docs">1.1. Create awesome docs!</h3>
<div class="paragraph">
<p><em>docToolchain</em> is an implementation of the <a href="https://www.writethedocs.org/guide/docs-as-code/"><em>docs-as-code</em></a> approach for software architecture plus some additional automation.
The basis of docToolchain is the philosophy that software documentation should be treated in the same way as code together with the <a href="https://arc42.org"><em>arc42</em></a> template for software architecture.</p>
</div>
<div class="paragraph">
<p>How it all began&#8230;&#8203;</p>
</div>
<div class="sect3">
<h4 id="_docs_as_code">1.1.1. docs-as-code</h4>
<div class="paragraph">
<p>Before this project started, I wasn&#8217;t aware of the term <em>docs-as-code</em>.
I just got tired of keeping all my architecture diagrams up to date by copying them from my UML tool over to my word processor.</p>
</div>
<div class="paragraph">
<p>As a lazy developer, I told myself &#8216;there has to be a better way of doing this&#8217;.
And I started to automate the diagram export and switched from a full fledged word processor over to a markup renderer.
This enabled me to reference the diagrams from within my text and update them just before I render the document.</p>
</div>
</div>
<div class="sect3">
<h4 id="_arc42">1.1.2. arc42</h4>
<div class="paragraph">
<p>Since my goal was to document software architectures, I was already using arc42, a template for software architecture documentation.
At that time, I used the MS Word template.</p>
</div>
<div class="paragraph">
<p>But what is arc42?</p>
</div>
<div class="paragraph">
<p>Dr. Gernot Starke and Peter Hruschka created this template in a joint effort to create a standard for software architecture documents.
They dumped all their experience about software architectures not only into a structure but also explaining texts.
These explanations are part of every chapter of the template and give you guidance on how to write each chapter of the document.</p>
</div>
<div class="paragraph">
<p>arc42 is available in many formats like MS Word, textile, and Confluence.
All these formats are automatically generated from one <em>golden master</em> formatted in <a href="https://asciidoctor.org/docs/asciidoc-syntax-quick-reference/"><em>AsciiDoc</em></a>.</p>
</div>
</div>
<div class="sect3">
<h4 id="_doctoolchain">1.1.3. docToolchain</h4>
<div class="paragraph">
<p>In order to follow the <em>docs-as-code</em> approach, you need a build script that automates steps like exporting diagrams and rendering the used Markdown (<em>AsciiDoc</em> in case of <em>docToolchain</em>) to the target format.</p>
</div>
<div class="paragraph">
<p>Unfortunately, such a build script is not easy to create in the first place (&#8216;how do I create .docx?&#8217;, &#8216;why does lib x not work with lib y?&#8217;) and it is also not too easy to maintain.</p>
</div>
<div class="paragraph">
<p><em>docToolchain</em> is the result of my journey through the <em>docs-as-code</em> land.
The goal is to have an easy to use build script that only has to be configured and not modified and that is maintained by a community as open source software.</p>
</div>
<div class="paragraph">
<p>The technical steps of my journey are written down in my blog: <a href="https://rdmueller.github.io" class="bare">https://rdmueller.github.io</a>.</p>
</div>
<hr>
<div class="paragraph">
<p>Let&#8217;s start with what you&#8217;ll get when you use <em>docToolchain</em>&#8230;&#8203;</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_benefits_of_the_docs_as_code_approach">1.2. Benefits of the docs-as-code Approach</h3>
<div class="paragraph">
<p>You want to write technical docs for your software project.
So it is likely you already have the tools and processes to work with source code in place.
Why not also use it for your docs?</p>
</div>
<div class="sect3">
<h4 id="_document_management_system">1.2.1. Document Management System</h4>
<div class="paragraph">
<p>By using a version control system like <a href="https://git-scm.com/">Git</a>, you get a perfect document management system for free.
It lets you version your docs, branch them and gives you an audit trail.
You are even able to check who wrote which part of the docs.
Isn&#8217;t that great?</p>
</div>
<div class="paragraph">
<p>Since your docs are now just plain text, it is also easy to do a diff and see exactly what has changed.</p>
</div>
<div class="paragraph">
<p>And when you store your docs in the same repository as your code, you always have both in sync!</p>
</div>
</div>
<div class="sect3">
<h4 id="_collaboration_and_review_process">1.2.2. Collaboration and Review Process</h4>
<div class="paragraph">
<p>Git as a distributed version control system even enables collaboration on your docs.
People can fork the docs and send you pull requests for the changes they made.
By reviewing the pull request, you have a perfect review process out of the box - by accepting the pull request, you show that you&#8217;ve reviewed and accepted the changes.
Most Git frontends like <a href="https://www.bitbucket.org">Bitbucket</a>, <a href="https://gitlab.com">GitLab</a> and of course <a href="https://github.com">GitHub</a> also allow you to reject pull requests with comments.</p>
</div>
</div>
<div class="sect3">
<h4 id="_image_references_and_code_snippets">1.2.3. Image References and Code Snippets</h4>
<div class="paragraph">
<p>Instead of pasting images to a binary document format, you now can reference images.
This will ensure that those images are always up to date every time you rebuild your documents.</p>
</div>
<div class="paragraph">
<p>In addition, you can reference code snippets directly from your source code.
This way, these snippets are also always up to date!</p>
</div>
</div>
<div class="sect3">
<h4 id="_compound_and_stakeholder_tailored_docs">1.2.4. Compound and Stakeholder-Tailored Docs</h4>
<div class="paragraph">
<p>Since you can not only reference images and code snippets but also sub-documents, you can split your docs into several sub-documents and a master, which brings all those docs together.
But you are not restricted to one master&#8201;&#8212;&#8201;you can create master docs for different stakeholders that only contain the chapters needed for them.</p>
</div>
</div>
<div class="sect3">
<h4 id="_many_more_features">1.2.5. Many more Features&#8230;&#8203;</h4>
<div class="paragraph">
<p>If you can dream it, you can script it.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Want to include a list of open issues from Jira?
<strong>✓</strong> Check.</p>
</li>
<li>
<p>Want to include a changelog from Git?
<strong>✓</strong> Check.</p>
</li>
<li>
<p>Want to use inline, text based diagrams?
<strong>✓</strong> Check.</p>
</li>
<li>
<p>and many more&#8230;&#8203;</p>
</li>
</ul>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_how_to_install_doctoolchain">2. How to install docToolchain</h2>
<div class="sectionbody">
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/02_install.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/02_install.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/d2c684ec9e55edec506308356b67016?d=identicon" alt="Jakub J Jablonski " width="32" title="Jakub J Jablonski "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/df21e0a05d621d59a147b9091a56ebdc?d=identicon" alt="Dr. Gernot Starke " width="32" title="Dr. Gernot Starke "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/2e3af23bd872d2b971788a4519e3bf46?d=identicon" alt="Jakub Jabłoński " width="32" title="Jakub Jabłoński "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5f528a3f6814d28b583f31842e3e8d9e?d=identicon" alt="Alexander Schwartz " width="32" title="Alexander Schwartz "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/34ef1437fd31ff96c31dff58125e93fe?d=identicon" alt="Dr. Stefan Pfeiffer " width="32" title="Dr. Stefan Pfeiffer "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/399cd7a37bd45f40c31dae5bb9f6cafe?d=identicon" alt="Jorge " width="32" title="Jorge "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/69124393772349d32fb9fe975545757d?d=identicon" alt="Lars Francke " width="32" title="Lars Francke "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/f1d121d3bcc84d897239a0b41224df68?d=identicon" alt="feinstaub " width="32" title="feinstaub "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>4 minutes to read</pre>
</div>
</div>
<div class="sect2">
<h3 id="_get_the_tool">2.1. Get the tool</h3>
<div class="paragraph">
<p>To start with docToolchain you need to get a copy of the current docToolchain repository.
The easiest way is to clone the repository without history and remove the <code>.git</code> folder:</p>
</div>
<div class="listingblock">
<div class="title">Linux with git clone</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">git clone --recursive https://github.com/docToolchain/docToolchain.git &lt;docToolchain home&gt;
cd &lt;docToolchain home&gt;
rm -rf .git
rm -rf resources/asciidoctor-reveal.js/.git
rm -rf resources/reveal.js/.git</code></pre>
</div>
</div>
<div class="paragraph">
<p><code>--recursive</code> option is required because the repository contains 2 submodules - <code>resources/asciidoctor-reveal.js</code> and <code>resources/reveal.js</code>.</p>
</div>
<div class="paragraph">
<p>Another way is to download the zipped git repository and rename it:</p>
</div>
<div class="listingblock">
<div class="title">Linux with download as zip</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">wget https://github.com/docToolchain/docToolchain/archive/master.zip
unzip master.zip

# fetching dependencies

cd docToolchain-master/resources

rm -d reveal.js
wget https://github.com/hakimel/reveal.js/archive/tags/3.3.0.zip -O reveal.js.zip
unzip reveal.js.zip
mv reveal.js-tags-3.3.0 reveal.js

rm -d asciidoctor-reveal.js
wget https://github.com/asciidoctor/asciidoctor-reveal.js/archive/9667f5c.zip -O asciidoctor-reveal.js.zip
unzip asciidoctor-reveal.js.zip
mv asciidoctor-reveal.js-9667f5c5d926b3be48361d6d6413d3896954894c asciidoctor-reveal.js

mv docToolchain-master &lt;docToolchain home&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you work (like me) on a Windows environment, just download and unzip the <a href="https://github.com/docToolchain/docToolchain/archive/master.zip">repository</a> as well as its dependencies:
<a href="https://github.com/hakimel/reveal.js/archive/tags/3.3.0.zip">reveal.js</a> and <a href="https://github.com/asciidoctor/asciidoctor-reveal.js/archive/9667f5c.zip">asciidoctor-reveal.js</a>.</p>
</div>
<div class="paragraph">
<p>After unzipping, put the dependencies in <code>resources</code> folder, so that the structure is the same as on <a href="https://github.com/docToolchain/docToolchain/tree/master/resources">GitHub</a>.</p>
</div>
<div class="paragraph">
<p>You can add &lt;docToolchain home&gt;/bin to your PATH or you can run doctoolchain with full path if you prefer.</p>
</div>
</div>
<div class="sect2">
<h3 id="_initialize_directory_for_documents">2.2. Initialize directory for documents</h3>
<div class="paragraph">
<p>The next step after getting docToolchain is to initialize a directory where your documents live.
In docToolchain this directory is named "newDocDir" during initialization, or just "docDir" later on.</p>
</div>
<div class="sect3">
<h4 id="_existing_documents">2.2.1. Existing documents</h4>
<div class="paragraph">
<p>If you already have some existing documents in AsciiDoc format in your project, you need to put the configuration file there to inform docToolchain what and how to process.
You can do that manually by copying the contents of template_config directory.
You can also do that by running <code>initExisting</code> task.</p>
</div>
<div class="listingblock">
<div class="title">Linux initExisting example</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">cd &lt;docToolchain home&gt;
./gradlew -b init.gradle initExisting -PnewDocDir=&lt;your directory&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>You need to open Config.groovy file and configure names of your files properly.
You may also change the PDF schema file to your taste.</p>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
docToolchain by default uses <code>src/docs</code> as the directory of your documentation.
If you use a different directory you need to go to &lt;docToolchain home&gt; and set the <code>inputPath</code> property in <code>gradle.properties</code> to <code>your/path</code>.
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_arc42_from_scratch">2.2.2. arc42 from scratch</h4>
<div class="paragraph">
<p>If you don&#8217;t have existing documents yet, or if you need a fresh start, you can get the <a href="https://arc42.org">arc42</a> template in AsciiDoc format.
You can do that by manually downloading from <a href="https://arc42.org/download" class="bare">https://arc42.org/download</a>.
You can also do that by running the
<code>initArc42&lt;language&gt;</code> task.
Currently supported languages are:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>DE - German</p>
</li>
<li>
<p>EN - English</p>
</li>
<li>
<p>ES - Spanish</p>
</li>
<li>
<p>RU - Russian</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="title">Linux initArc42EN example</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">cd &lt;docToolchain home&gt;
./gradlew -b init.gradle initArc42EN -PnewDocDir=&lt;newDocDir&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The Config.groovy file is then preconfigured to use the downloaded template.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Blog-Post: <a href="https://rdmueller.github.io/arc42/">Let&#8217;s add Content!</a>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_build">2.3. Build</h3>
<div class="paragraph">
<p>This should already be enough to start a first build.</p>
</div>
<div class="listingblock">
<div class="title">Linux</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">doctoolchain &lt;docDir&gt; generateHTML
doctoolchain &lt;docDir&gt; generatePDF</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">Windows</div>
<div class="content">
<pre class="CodeRay highlight"><code>doctoolchain.bat &lt;docDir&gt; generateHTML
doctoolchain.bat &lt;docDir&gt; generatePDF</code></pre>
</div>
</div>
<div class="paragraph">
<p>&lt;docDir&gt; may be relative, e.g. ".", or absolute.</p>
</div>
<div class="paragraph">
<p>As a result, you will see the progress of your build together with some warnings which you can just ignore for the moment.</p>
</div>
<div class="paragraph">
<p>The first build generated some files within the <code>&lt;docDir&gt;/build</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code>build
|-- html5
|   |-- arc42-template.html
|   `-- images
|       |-- 05_building_blocks-EN.png
|       |-- 08-Crosscutting-Concepts-Structure-EN.png
|       `-- arc42-logo.png
`-- pdf
    |-- arc42-template.pdf
    `-- images
        |-- 05_building_blocks-EN.png
        |-- 08-Crosscutting-Concepts-Structure-EN.png
        `-- arc42-logo.png</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Congratulations!</strong> If you see the same folder structure, you just managed to render the standard arc42 template as html and pdf!</p>
</div>
<div class="paragraph">
<p>If you didn&#8217;t get the right output, please raise an issue on <a href="https://github.com/docToolchain/docToolchain/issues">github</a></p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Blog-Posts: <a href="https://rdmueller.github.io/enterprise-edition/">Behind the great Firewall</a>, <a href="https://rdmueller.github.io/enterprise-edition2/">Enterprise AsciiDoctor</a>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_publish_to_confluence">2.4. Publish to Confluence</h3>
<div class="paragraph">
<p>In addition to <code>Config.groovy</code> there is also a <code>scripts/ConfluenceConfig.groovy</code> file.
If you are not using Confluence you can remove it.
If you use Confluence, then you need to open this file and adapt it to your environment.
You can also create multiple copies of that file.
For example you can have <code>ConfluenceConfig.groovy</code> for publishing official pages, and <code>MyConfluenceConfig.groovy</code> with a different Confluence space for reviews.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">doctoolchain &lt;docDir&gt; publishToConfluence</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_overview_of_available_tasks">3. Overview of available Tasks</h2>
<div class="sectionbody">
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_tasks.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_tasks.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/34ef1437fd31ff96c31dff58125e93fe?d=identicon" alt="Dr. Stefan Pfeiffer " width="32" title="Dr. Stefan Pfeiffer "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/69124393772349d32fb9fe975545757d?d=identicon" alt="Lars Francke " width="32" title="Lars Francke "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>2 minutes to read</pre>
</div>
</div>
<div class="paragraph">
<p>This chapter explains all docToolchain specific tasks.</p>
</div>
<div class="paragraph">
<p>The following picture gives an overview of the whole build system:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/ea/Manual/Overview2.png" alt="Overview2">
</div>
<div class="title">Figure 1. docToolchain</div>
</div>
<div class="sect2">
<h3 id="_conventions">3.1. Conventions</h3>
<div class="paragraph">
<p>There are some simple naming conventions for the tasks.
They might be confusing at first and that&#8217;s why they are explained here.</p>
</div>
<div class="sect3">
<h4 id="_generatex">3.1.1. generateX</h4>
<div class="paragraph">
<p><em>render</em> would have been another good prefix, since these tasks use the plain AsciiDoctor functionality to render the source to a given format.</p>
</div>
</div>
<div class="sect3">
<h4 id="_exportx">3.1.2. exportX</h4>
<div class="paragraph">
<p>These tasks export images and AsciiDoc snippets from other systems or file formats.
The resulting artifacts can then be included from your main sources.</p>
</div>
<div class="paragraph">
<p>What&#8217;s different from the <em>generateX</em> tasks is that you don&#8217;t need to export with each build.</p>
</div>
<div class="paragraph">
<p>It is also likely that you have to put the resulting artifacts under version control because the tools needed for the export (like Sparx Enterprise Architect or MS PowerPoint) are likely to be not available on a build server or on another contributor&#8217;s machine.</p>
</div>
</div>
<div class="sect3">
<h4 id="_converttox">3.1.3. convertToX</h4>
<div class="paragraph">
<p>These tasks take the output from AsciiDoctor and convert it (through other tools) to the target format.
This results in a dependency on a <em>generateX</em> task and another external tool (currently <a href="https://pandoc.org">pandoc</a>).</p>
</div>
</div>
<div class="sect3">
<h4 id="_publishtox">3.1.4. publishToX</h4>
<div class="paragraph">
<p>These tasks not only convert your documents but also deploy/publish/move them to a remote system&#8201;&#8212;&#8201;currently Confluence.
This means that the result is likely to be visible immediately to others.</p>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect2">
<h3 id="_generatehtml">3.2. generateHTML</h3>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_generateHTML.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_generateHTML.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5f528a3f6814d28b583f31842e3e8d9e?d=identicon" alt="Alexander Schwartz " width="32" title="Alexander Schwartz "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/73252f762a6ec69bb59a23c8ff861c4a?d=identicon" alt="David M. Carr " width="32" title="David M. Carr "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/34ef1437fd31ff96c31dff58125e93fe?d=identicon" alt="Dr. Stefan Pfeiffer " width="32" title="Dr. Stefan Pfeiffer "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/69124393772349d32fb9fe975545757d?d=identicon" alt="Lars Francke " width="32" title="Lars Francke "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>3 minutes to read</pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="images/ea/Manual/generateHTML.png" alt="generateHTML">
</div>
</div>
<div class="paragraph">
<p>This is the standard AsciiDoctor generator which is supported out of the box.</p>
</div>
<div class="paragraph">
<p>The result is written to <code>build/html5</code>.
The HTML files need the images folder to be in the same directory to display correctly.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
If you would like to have a single-file HTML as result, you can configure AsciiDoctor to store the images inline as <code>data-uri</code>.<br>
Just set <code>:data-uri:</code> in the config of your AsciiDoc file.<br>
But be warned - such a file can become very big easily and some browsers might get into trouble rendering them.<br>
<a href="https://rdmueller.github.io/single-file-html/" class="bare">https://rdmueller.github.io/single-file-html/</a>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_text_based_diagrams">3.2.1. Text based Diagrams</h4>
<div class="paragraph">
<p>For docToolchain, it is configured to use the <a href="https://asciidoctor.org/docs/asciidoctor-diagram/">asciidoctor-diagram</a> plugin which is used to create PlantUML diagrams.</p>
</div>
<div class="paragraph">
<p>The plugin also supports a bunch of other text based diagrams, but <a href="http://plantuml.com/">PlantUML</a> is the most used.</p>
</div>
<div class="paragraph">
<p>To use it, just specify your PlantUML code like this:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>.example diagram
[plantuml, "{plantUMLDir}demoPlantUML", png] <i class="conum" data-value="1"></i><b>(1)</b>
----
class BlockProcessor
class DiagramBlock
class DitaaBlock
class PlantUmlBlock

BlockProcessor &lt;|-- DiagramBlock
DiagramBlock &lt;|-- DitaaBlock
DiagramBlock &lt;|-- PlantUmlBlock
----</pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The element of this list specifies the diagram tool <code>plantuml</code> to be used.<br>
The second element is the name of the image to be created and the third specifies the image type.</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
The <code>{plantUMLDir}</code> ensures that PlantUML also works for the <a href="#_generatepdf">generatePDF</a> task.
Without it, <a href="#_generatehtml">generateHTML</a> works fine, but the PDF will not contain the generated images.
</td>
</tr>
</table>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
Make sure to specify a unique image name for each diagram, otherwise they will overwrite each other.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The above example renders as</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/demoPlantUML.png" alt="demoPlantUML" width="246" height="236">
</div>
<div class="title">Figure 2. example diagram</div>
</div>
<div class="paragraph">
<p>If you want to control the size of the generated diagram in the output, you can configure the "width" attribute (in pixels) or "scale" attribute (floating point ratio) passed to <a href="https://asciidoctor.org/docs/asciidoctor-diagram/">asciidoctor-diagram</a>.
For example, if you take the example diagram above and change the declaration to one of the below versions</p>
</div>
<div class="literalblock">
<div class="content">
<pre>[plantuml, target="{plantUMLDir}demoPlantUMLWidth", format=png, width=250]
# rest of the diagram definition

[plantuml, target="{plantUMLDir}demoPlantUMLScale", format=png, scale=0.75]
# rest of the diagram definition</pre>
</div>
</div>
<div class="paragraph">
<p>it will render like this:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/demoPlantUMLWidth.png" alt="demoPlantUMLWidth" width="250" height="236">
</div>
<div class="title">Figure 3. example diagram (with specified width)</div>
</div>
<div class="imageblock">
<div class="content">
<img src="images/demoPlantUMLScale.png" alt="demoPlantUMLScale" width="184" height="177">
</div>
<div class="title">Figure 4. example diagram (with specified scale)</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
PlantUML needs Graphviz dot installed to work.
If you can&#8217;t install it, you can use the Java based version of the dot library.
Just add <code>!pragma graphviz_dot jdot</code> as the first line of your diagram definition.
This is still an experimental feature, but already works quite well!<br>
<a href="https://rdmueller.github.io/plantuml-without-graphviz/" class="bare">https://rdmueller.github.io/plantuml-without-graphviz/</a>
</td>
</tr>
</table>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Blog-Posts: <a href="https://rdmueller.github.io/plantuml-gradle/">PlantUML with Gradle</a>, <a href="https://rdmueller.github.io/plantUML-and-pdf/">plantUML with Asciidoctor-pdf</a>, <a href="https://rdmueller.github.io/plantUML-revisited/">plantUML revisited</a>, <a href="https://rdmueller.github.io/plantuml-without-graphviz/">How to use PlantUML without Graphviz</a>
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_source">3.2.2. Source</h4>
<div class="listingblock">
<div class="title">AsciiDocBasics.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">task generateHTML (
        <span class="key">type</span>: AsciidoctorTask,
        <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">docToolchain</span><span class="delimiter">'</span></span>,
        <span class="key">description</span>: <span class="string"><span class="delimiter">'</span><span class="content">use html5 as asciidoc backend</span><span class="delimiter">'</span></span>) {

    attributes (
        <span class="string"><span class="delimiter">'</span><span class="content">plantUMLDir</span><span class="delimiter">'</span></span>         : <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
    )
    onlyIf {
        !sourceFiles.findAll {
            <span class="string"><span class="delimiter">'</span><span class="content">html</span><span class="delimiter">'</span></span> <span class="keyword">in</span> <span class="local-variable">it</span>.formats
        }.empty
    }

    sources {
        sourceFiles.findAll {
            <span class="string"><span class="delimiter">'</span><span class="content">html</span><span class="delimiter">'</span></span> <span class="keyword">in</span> <span class="local-variable">it</span>.formats
        }.each {
            include <span class="local-variable">it</span>.file
            println <span class="local-variable">it</span>.file
        }
    }

    backends = [<span class="string"><span class="delimiter">'</span><span class="content">html5</span><span class="delimiter">'</span></span>]
}</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect2">
<h3 id="_fixencoding">3.3. fixEncoding</h3>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_fixencoding.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_fixencoding.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/5f528a3f6814d28b583f31842e3e8d9e?d=identicon" alt="Alexander Schwartz " width="32" title="Alexander Schwartz "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>1 minute to read</pre>
</div>
</div>
<div class="paragraph">
<p>Whenever Asciidoctor has to process a file which is not UTF-8 encoded, the underlying Ruby tries to read it and throws an error like this:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>asciidoctor: FAILED: /home/demo/test.adoc: Failed to load AsciiDoc document - invalid byte sequence in UTF-8</pre>
</div>
</div>
<div class="paragraph">
<p>Unfortunately, it is hard to find the wrong encoded file if a lot of <code>includes::</code> are used - Asciidoctor only shows the name of the main document.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
This is not a problem of Asciidoctor, but of the underlying ruby interpreter.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>This target crawls through all <code>*.ad</code> and <code>*.adoc</code> files and checks their encoding.
If it encounters a file which is not UTF-8 encoded, it will rewrite it with the UTF-8 encoding.</p>
</div>
<div class="listingblock">
<div class="title">scripts/fixEncoding.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">groovy.util.*</span>
<span class="keyword">import</span> <span class="include">static</span> <span class="include">groovy.io.FileType.*</span>

task fixEncoding(
        <span class="key">description</span>: <span class="string"><span class="delimiter">'</span><span class="content">finds and converts non UTF-8 adoc files to UTF-8</span><span class="delimiter">'</span></span>,
        <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">docToolchain helper</span><span class="delimiter">'</span></span>,
) {
    doLast {
        <span class="predefined-type">File</span> sourceFolder = <span class="keyword">new</span> <span class="predefined-type">File</span>(<span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">${</span>docDir<span class="inline-delimiter">}</span></span><span class="content">/</span><span class="inline"><span class="inline-delimiter">${</span>inputPath<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>)
        println(<span class="string"><span class="delimiter">&quot;</span><span class="content">sourceFolder: </span><span class="delimiter">&quot;</span></span> + sourceFolder.canonicalPath)
        sourceFolder.traverse(<span class="key">type</span>: FILES) { file -&gt;
            <span class="keyword">if</span> (file.name ==~ <span class="string"><span class="delimiter">'</span><span class="content">^.*(ad|adoc|asciidoc)$</span><span class="delimiter">'</span></span>) {
                CharsetToolkit toolkit = <span class="keyword">new</span> CharsetToolkit(file);
                <span class="comment">// guess the encoding</span>
                <span class="keyword">def</span> guessedCharset = toolkit.getCharset().toString().toUpperCase();
                <span class="keyword">if</span> (guessedCharset!=<span class="string"><span class="delimiter">'</span><span class="content">UTF-8</span><span class="delimiter">'</span></span>) {
                    <span class="keyword">def</span> text = file.text
                    file.write(text, <span class="string"><span class="delimiter">&quot;</span><span class="content">utf-8</span><span class="delimiter">&quot;</span></span>)
                    println(<span class="string"><span class="delimiter">&quot;</span><span class="content">  converted </span><span class="inline"><span class="inline-delimiter">${</span>file.name<span class="inline-delimiter">}</span></span><span class="content"> from '</span><span class="inline"><span class="inline-delimiter">${</span>guessedCharset<span class="inline-delimiter">}</span></span><span class="content">' to 'UFT-8'</span><span class="delimiter">&quot;</span></span>)
                }
            }
        }
    }
}</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
<div class="sect2">
<h3 id="_prependfilename">3.4. prependFilename</h3>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_prependFilename.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_prependFilename.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/5f528a3f6814d28b583f31842e3e8d9e?d=identicon" alt="Alexander Schwartz " width="32" title="Alexander Schwartz "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/eedf6f2d4b31cc4e49a7aa4fc55f51d3?d=identicon" alt="Ralf D. Müller " width="32" title="Ralf D. Müller "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>1 minute to read</pre>
</div>
</div>
<div class="paragraph">
<p>When Asciidoctor renders a file, the file context only knows the name of the top-level AsciiDoc file but an include file doesn&#8217;t know that it is being included.
It will simply get the name of the master file and has no chance to get his own names as attribute.</p>
</div>
<div class="paragraph">
<p>This task simply crawls through all AsciiDoc files and prepends the name of the current file like this:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>:filename: manual/03_task_prependFilename.adoc</pre>
</div>
</div>
<div class="paragraph">
<p>This way, each file can get its own file name.
This enables features like the inclusion of file contributors (see exportContributors-task).</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
The task skips all files named <code>config.*</code>, <code>_config.*</code>, <code>feedback.*</code> and <code>_feedback.*</code>.
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="title">scripts/prependFilename.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">static</span> <span class="include">groovy.io.FileType.*</span>

task prependFilename(
        <span class="key">description</span>: <span class="string"><span class="delimiter">'</span><span class="content">crawls through all AsciiDoc files and prepends the name of the current file</span><span class="delimiter">'</span></span>,
        <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">docToolchain helper</span><span class="delimiter">'</span></span>,
) {
    doLast {
        <span class="predefined-type">File</span> sourceFolder = <span class="keyword">new</span> <span class="predefined-type">File</span>(<span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">${</span>docDir<span class="inline-delimiter">}</span></span><span class="content">/</span><span class="inline"><span class="inline-delimiter">${</span>inputPath<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>)
        println(<span class="string"><span class="delimiter">&quot;</span><span class="content">sourceFolder: </span><span class="delimiter">&quot;</span></span> + sourceFolder.canonicalPath)
        sourceFolder.traverse(<span class="key">type</span>: FILES) { file -&gt;
            <span class="keyword">if</span> (file.name ==~ <span class="string"><span class="delimiter">'</span><span class="content">^.*(ad|adoc|asciidoc)$</span><span class="delimiter">'</span></span>) {
                <span class="keyword">if</span> (file.name.split(<span class="string"><span class="delimiter">'</span><span class="content">[.]</span><span class="delimiter">'</span></span>)[<span class="integer">0</span>] <span class="keyword">in</span> [<span class="string"><span class="delimiter">&quot;</span><span class="content">feedback</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">_feedback</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">config</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">_config</span><span class="delimiter">&quot;</span></span>]) {
                    println <span class="string"><span class="delimiter">&quot;</span><span class="content">skipped </span><span class="delimiter">&quot;</span></span>+file.name
                } <span class="keyword">else</span> {
                    <span class="keyword">def</span> text = file.getText(<span class="string"><span class="delimiter">'</span><span class="content">utf-8</span><span class="delimiter">'</span></span>)
                    <span class="keyword">def</span> name = file.canonicalPath - sourceFolder.canonicalPath
                    name = name.replace(<span class="string"><span class="delimiter">&quot;</span><span class="char">\\</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">/</span><span class="delimiter">&quot;</span></span>).replaceAll(<span class="string"><span class="delimiter">&quot;</span><span class="content">^/</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span>)
                    <span class="keyword">if</span> (text.contains(<span class="string"><span class="delimiter">&quot;</span><span class="content">:filename:</span><span class="delimiter">&quot;</span></span>)) {
                        text = text.replaceAll(<span class="string"><span class="delimiter">&quot;</span><span class="content">:filename:.*</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">:filename: </span><span class="inline"><span class="inline-delimiter">$</span>name</span><span class="delimiter">&quot;</span></span>)
                        println <span class="string"><span class="delimiter">&quot;</span><span class="content">updated </span><span class="delimiter">&quot;</span></span>+name
                    } <span class="keyword">else</span> {
                        text = <span class="string"><span class="delimiter">&quot;</span><span class="content">:filename: </span><span class="inline"><span class="inline-delimiter">$</span>name</span><span class="char">\n</span><span class="delimiter">&quot;</span></span> + text
                        println <span class="string"><span class="delimiter">&quot;</span><span class="content">added   </span><span class="delimiter">&quot;</span></span>+name
                    }
                    file.write(text,<span class="string"><span class="delimiter">'</span><span class="content">utf-8</span><span class="delimiter">'</span></span>)
                }
            }
        }
    }
}</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
<div class="sect2">
<h3 id="_collectincludes">3.5. collectIncludes</h3>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_collectIncludes.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_collectIncludes.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Müller " width="32" title="Ralf D. Müller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5f528a3f6814d28b583f31842e3e8d9e?d=identicon" alt="Alexander Schwartz " width="32" title="Alexander Schwartz "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/c8d80df8d69ee21f98fc9a9bfa527897?d=identicon" alt="Julian Elve " width="32" title="Julian Elve "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/3c015f3ee9960d842c195993cbb91cff?d=identicon" alt="Tim Riemer " width="32" title="Tim Riemer "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>2 minutes to read</pre>
</div>
</div>
<div class="paragraph">
<p>This tasks crawls through your whole project looking for AsciiDoc files with a certain name pattern.
It then creates an AsciiDoc file which just includes all files found.</p>
</div>
<div class="paragraph">
<p>When you create modular documentation, most includes are static.
For example, the arc42-template has 12 chapters and a master template in which those 12 chapters are included.</p>
</div>
<div class="paragraph">
<p>But when you work with dynamic modules like ADRs - Architecture Decision Records, you create those files on the fly.
Maybe not even within your <code>/src/docs</code> folder but right beside the code file for which you wrote the ADR.</p>
</div>
<div class="paragraph">
<p>In order to include these files in your documentation, you now would have to add the file with its whole relative path to one of your AsciiDoc files.</p>
</div>
<div class="paragraph">
<p>This task will handle it for you!</p>
</div>
<div class="paragraph">
<p>Just stick with your files names to the pattern <code>^[A-Z]{3,}-.*</code> (it begins with at last three upper case letters and a dash) and this task will collect this file and write it to your build folder. You only have to include this generated file from within your documentation.</p>
</div>
<div class="paragraph">
<p>Example:</p>
</div>
<div class="paragraph">
<p>You have a file called</p>
</div>
<div class="literalblock">
<div class="content">
<pre>/src/java/yourCompany/domain/books/ADR-1-whyWeUseTheAISINInsteadOFISBN.adoc</pre>
</div>
</div>
<div class="paragraph">
<p>The task will collect this file and write another file called</p>
</div>
<div class="literalblock">
<div class="content">
<pre>/build/docs/_includes/ADR_includes.adoc</pre>
</div>
</div>
<div class="paragraph">
<p>Which will look like this:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>include::../../../src/java/yourCompany/domain/books/ADR-1-whyWeUseTheAISINInsteadOFISBN.adoc[]</pre>
</div>
</div>
<div class="paragraph">
<p>Obviously, you get the most benefit if you not only have one ADR file, but several ones which get collected.
😎</p>
</div>
<div class="paragraph">
<p>You then include these files in your main documentation by using a single include:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>include::{targetDir}/docs/_includes/ADR_includes.adoc[]</pre>
</div>
</div>
<div class="listingblock">
<div class="title">scripts/collectIncludes.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">static</span> <span class="include">groovy.io.FileType.*</span>
<span class="keyword">import</span> <span class="include">java.security.MessageDigest</span>

task collectIncludes(
        <span class="key">description</span>: <span class="string"><span class="delimiter">'</span><span class="content">collect all ADRs as includes in one file</span><span class="delimiter">'</span></span>,
        <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">docToolchain</span><span class="delimiter">'</span></span>
) {
    doFirst {
        <span class="keyword">new</span> <span class="predefined-type">File</span>(targetDir, <span class="string"><span class="delimiter">'</span><span class="content">_includes</span><span class="delimiter">'</span></span>).mkdirs()
    }
    doLast {
        <span class="comment">//let's search the whole project for files, not only the docs folder</span>
        <span class="comment">//could be a problem with node projects :-)</span>

        <span class="comment">//running as subproject? set scandir to main project</span>
        <span class="keyword">if</span> (project.name!=rootProject.name &amp;&amp; scanDir==<span class="string"><span class="delimiter">'</span><span class="content">.</span><span class="delimiter">'</span></span>) {
            scanDir = project(<span class="string"><span class="delimiter">'</span><span class="content">:</span><span class="delimiter">'</span></span>).projectDir.path
        }
        <span class="keyword">if</span> (docDir.startsWith(<span class="string"><span class="delimiter">'</span><span class="content">.</span><span class="delimiter">'</span></span>)) {
            docDir = file(<span class="keyword">new</span> <span class="predefined-type">File</span>(projectDir, docDir).canonicalPath)
        }
        logger.info <span class="string"><span class="delimiter">&quot;</span><span class="content">docToolchain&gt; docDir: </span><span class="inline"><span class="inline-delimiter">${</span>docDir<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>
        logger.info <span class="string"><span class="delimiter">&quot;</span><span class="content">docToolchain&gt; scanDir: </span><span class="inline"><span class="inline-delimiter">${</span>scanDir<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>
        <span class="keyword">if</span> (scanDir.startsWith(<span class="string"><span class="delimiter">'</span><span class="content">.</span><span class="delimiter">'</span></span>)) {
            scanDir = file(<span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, scanDir).canonicalPath)
        } <span class="keyword">else</span> {
            scanDir = file(<span class="keyword">new</span> <span class="predefined-type">File</span>(scanDir, <span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span>).canonicalPath)
        }
        logger.info <span class="string"><span class="delimiter">&quot;</span><span class="content">docToolchain&gt; scanDir: </span><span class="inline"><span class="inline-delimiter">${</span>scanDir<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>

        logger.info <span class="string"><span class="delimiter">&quot;</span><span class="content">docToolchain&gt; includeRoot: </span><span class="inline"><span class="inline-delimiter">${</span>includeRoot<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>

        <span class="keyword">if</span> (includeRoot.startsWith(<span class="string"><span class="delimiter">'</span><span class="content">.</span><span class="delimiter">'</span></span>)) {
            includeRoot = file(<span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, includeRoot).canonicalPath)
        }
        logger.info <span class="string"><span class="delimiter">&quot;</span><span class="content">docToolchain&gt; includeRoot: </span><span class="inline"><span class="inline-delimiter">${</span>includeRoot<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>

        <span class="predefined-type">File</span> sourceFolder = scanDir
        println <span class="string"><span class="delimiter">&quot;</span><span class="content">sourceFolder: </span><span class="delimiter">&quot;</span></span> + sourceFolder.canonicalPath
        <span class="keyword">def</span> collections = [:]
        sourceFolder.traverse(<span class="key">type</span>: FILES) { file -&gt;
            <span class="keyword">if</span> (file.name ==~ <span class="string"><span class="delimiter">'</span><span class="content">^[A-Z]{3,}-.*[.](ad|adoc|asciidoc)$</span><span class="delimiter">'</span></span>) {
                <span class="keyword">def</span> type = file.name.replaceAll(<span class="string"><span class="delimiter">'</span><span class="content">^([A-Z]{3,})-.*$</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">\$1</span><span class="delimiter">'</span></span>)
                <span class="keyword">if</span> (!collections[type]) {
                    collections[type] = <span class="type">[]</span>
                }
                println <span class="string"><span class="delimiter">&quot;</span><span class="content">file: </span><span class="delimiter">&quot;</span></span> + file.canonicalPath
                <span class="keyword">def</span> fileName = (file.canonicalPath - scanDir.canonicalPath)[<span class="integer">1</span>..-<span class="integer">1</span>]
                println <span class="string"><span class="delimiter">&quot;</span><span class="content">corrected file: </span><span class="delimiter">&quot;</span></span> + fileName
                <span class="keyword">if</span> (fileName.startsWith(<span class="string"><span class="delimiter">'</span><span class="content">docToolchain</span><span class="delimiter">'</span></span>)) {
                    <span class="comment">//ignore docToolchain as submodule</span>
                } <span class="keyword">else</span> {
                    collections[type] &lt;&lt; fileName
                }
            }
        }
        println <span class="string"><span class="delimiter">&quot;</span><span class="content">targetDir - docDir: </span><span class="delimiter">&quot;</span></span> + (targetDir - docDir)
        println <span class="string"><span class="delimiter">&quot;</span><span class="content">targetDir - includeRoot: </span><span class="delimiter">&quot;</span></span> + (targetDir - includeRoot)
        <span class="keyword">def</span> pathDiff = <span class="string"><span class="delimiter">'</span><span class="content">../</span><span class="delimiter">'</span></span> * ((targetDir - docDir)
                                    .replaceAll(<span class="string"><span class="delimiter">'</span><span class="content">^/</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>)
                                    .replaceAll(<span class="string"><span class="delimiter">'</span><span class="content">/$</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>)
                                    .replaceAll(<span class="string"><span class="delimiter">&quot;</span><span class="content">[^/]</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>).size()+<span class="integer">1</span>)

        println <span class="string"><span class="delimiter">&quot;</span><span class="content">pathDiff: </span><span class="delimiter">&quot;</span></span> + pathDiff
        collections.each { type, fileNames -&gt;
            <span class="keyword">if</span> (fileNames) {
                <span class="keyword">def</span> outFile = <span class="keyword">new</span> <span class="predefined-type">File</span>(targetDir+<span class="string"><span class="delimiter">'</span><span class="content">/_includes</span><span class="delimiter">'</span></span>, type+<span class="string"><span class="delimiter">'</span><span class="content">_includes.adoc</span><span class="delimiter">'</span></span>)
                println outFile.canonicalPath-sourceFolder.canonicalPath
                outFile.write(<span class="string"><span class="delimiter">&quot;</span><span class="content">// this is autogenerated</span><span class="char">\n</span><span class="delimiter">&quot;</span></span>)
                fileNames.sort().each { fileName -&gt;
                    outFile.append (<span class="string"><span class="delimiter">&quot;</span><span class="content">include::../</span><span class="delimiter">&quot;</span></span>+pathDiff+fileName.replace(<span class="string"><span class="delimiter">&quot;</span><span class="char">\\</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">/</span><span class="delimiter">&quot;</span></span>)+<span class="string"><span class="delimiter">&quot;</span><span class="content">[]</span><span class="char">\n</span><span class="char">\n</span><span class="delimiter">&quot;</span></span>)
                }
            }
        }
    }
}</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
<div class="sect2">
<h3 id="_generatepdf">3.6. generatePDF</h3>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_generatePDF.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_generatePDF.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/34ef1437fd31ff96c31dff58125e93fe?d=identicon" alt="Dr. Stefan Pfeiffer " width="32" title="Dr. Stefan Pfeiffer "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5f528a3f6814d28b583f31842e3e8d9e?d=identicon" alt="Alexander Schwartz " width="32" title="Alexander Schwartz "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/69124393772349d32fb9fe975545757d?d=identicon" alt="Lars Francke " width="32" title="Lars Francke "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/eedf6f2d4b31cc4e49a7aa4fc55f51d3?d=identicon" alt="Ralf D. Müller " width="32" title="Ralf D. Müller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5107bc93f3e4d103ca06f4e68a64b38?d=identicon" alt="stehlih " width="32" title="stehlih "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>2 minutes to read</pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="images/ea/Manual/generatePDF.png" alt="generatePDF">
</div>
</div>
<div class="paragraph">
<p>This task makes use of the <a href="https://asciidoctor.org/docs/convert-asciidoc-to-pdf/">asciidoctor-pdf</a> plugin to render your documents as a pretty PDF file.</p>
</div>
<div class="paragraph">
<p>The file will be written to <code>build/pdf</code>.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
The used plugin is still in alpha status, but the results are already quite good.
If you want to use another way to create a PDF, use <a href="https://phantomjs.org/">PhantomJS</a> for instance and script it.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The PDF is generated directly from your AsciiDoc sources without the need of an intermediate format or other tools.
The result looks more like a nicely rendered book than a print-to-pdf HTML page.</p>
</div>
<div class="paragraph">
<p>It is very likely that you need to "theme" you PDF - change colors, fonts, page header, and footer.
This can be done by creating a <code>custom-theme.yml</code> file.
As a starting point, copy the file <code>src/docs/pdfTheme/custom-theme.yml</code> from docToolchain to your project and reference it from your main <code>.adoc`file via setting the `:pdf-stylesdir:</code>.
For instance, insert</p>
</div>
<div class="literalblock">
<div class="content">
<pre>:pdf-stylesdir: ../pdfTheme</pre>
</div>
</div>
<div class="paragraph">
<p>at the top of your document to reference the <code>custom-theme.yml</code> from the <code>/pdfTheme</code> folder.</p>
</div>
<div class="paragraph">
<p>Documentation on how to modify a theme can be found in the <a href="https://github.com/asciidoctor/asciidoctor-pdf/blob/master/docs/theming-guide.adoc">asciidoctor-pdf theming guide</a>.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Blog-Post: <a href="https://rdmueller.github.io/pdf-output/">Beyond HTML</a>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_source_2">3.6.1. Source</h4>
<div class="listingblock">
<div class="title">AsciiDocBasics.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">task generatePDF (
        <span class="key">type</span>: AsciidoctorTask,
        <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">docToolchain</span><span class="delimiter">'</span></span>,
        <span class="key">description</span>: <span class="string"><span class="delimiter">'</span><span class="content">use pdf as asciidoc backend</span><span class="delimiter">'</span></span>) {

    attributes (
        <span class="string"><span class="delimiter">'</span><span class="content">plantUMLDir</span><span class="delimiter">'</span></span>         : file(<span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">${</span>docDir<span class="inline-delimiter">}</span></span><span class="content">/</span><span class="inline"><span class="inline-delimiter">${</span>config.outputPath<span class="inline-delimiter">}</span></span><span class="content">/images/plantUML/</span><span class="delimiter">&quot;</span></span>).path
    )
    onlyIf {
        !sourceFiles.findAll {
            <span class="string"><span class="delimiter">'</span><span class="content">pdf</span><span class="delimiter">'</span></span> <span class="keyword">in</span> <span class="local-variable">it</span>.formats
        }.empty
    }

    sources {
        sourceFiles.findAll {
            <span class="string"><span class="delimiter">'</span><span class="content">pdf</span><span class="delimiter">'</span></span> <span class="keyword">in</span> <span class="local-variable">it</span>.formats
        }.each {
            include <span class="local-variable">it</span>.file
        }
    }

    backends = [<span class="string"><span class="delimiter">'</span><span class="content">pdf</span><span class="delimiter">'</span></span>]
}</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect2">
<h3 id="_generatedocbook">3.7. generateDocbook</h3>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_generateDocBook.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_generateDocBook.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>1 minute to read</pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="images/ea/Manual/generateDocBook.png" alt="generateDocBook">
</div>
</div>
<div class="paragraph">
<p>This is only a helper task - it generates the intermediate format for <a href="#_converttodocx">convertToDocx</a> and <a href="#_converttoepub">convertToEpub</a>.</p>
</div>
<div class="sect3">
<h4 id="_source_3">3.7.1. Source</h4>
<div class="listingblock">
<div class="title">AsciiDocBasics.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">task generateDocbook (
        <span class="key">type</span>: AsciidoctorTask,
        <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">docToolchain</span><span class="delimiter">'</span></span>,
        <span class="key">description</span>: <span class="string"><span class="delimiter">'</span><span class="content">use docbook as asciidoc backend</span><span class="delimiter">'</span></span>) {

    onlyIf {
        !sourceFiles.findAll {
            <span class="string"><span class="delimiter">'</span><span class="content">docbook</span><span class="delimiter">'</span></span> <span class="keyword">in</span> <span class="local-variable">it</span>.formats
        }.empty
    }

    sources {
        sourceFiles.findAll {
            <span class="string"><span class="delimiter">'</span><span class="content">docbook</span><span class="delimiter">'</span></span> <span class="keyword">in</span> <span class="local-variable">it</span>.formats
        }.each {
            include <span class="local-variable">it</span>.file
        }
    }

    backends = [<span class="string"><span class="delimiter">'</span><span class="content">docbook</span><span class="delimiter">'</span></span>]
}</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect2">
<h3 id="_generatedeck">3.8. generateDeck</h3>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_generateDeck.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_generateDeck.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/34ef1437fd31ff96c31dff58125e93fe?d=identicon" alt="Dr. Stefan Pfeiffer " width="32" title="Dr. Stefan Pfeiffer "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>1 minute to read</pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="images/ea/Manual/generateDeck.png" alt="generateDeck">
</div>
</div>
<div class="paragraph">
<p>This task makes use of the <a href="https://github.com/asciidoctor/asciidoctor-reveal.js/">asciidoctor-reveal.js</a> backend to render your documents into a HTML based presentation.</p>
</div>
<div class="paragraph">
<p>This task is best used together with the <a href="#_exportppt">exportPPT</a> task.
It creates a PowerPoint presentation and enriches it with reveal.js slide definitions in AsciiDoc within the speaker notes.</p>
</div>
<div class="sect3">
<h4 id="_source_4">3.8.1. Source</h4>
<div class="listingblock">
<div class="title">AsciiDocBasics.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">task generateDeck (
        <span class="key">type</span>: AsciidoctorTask,
        <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">docToolchain</span><span class="delimiter">'</span></span>,
        <span class="key">description</span>: <span class="string"><span class="delimiter">'</span><span class="content">use revealJs as asciidoc backend to create a presentation</span><span class="delimiter">'</span></span>) {

    attributes (
            <span class="string"><span class="delimiter">'</span><span class="content">plantUMLDir</span><span class="delimiter">'</span></span>  : <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>,
            <span class="string"><span class="delimiter">'</span><span class="content">idprefix</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">slide-</span><span class="delimiter">'</span></span>,
            <span class="string"><span class="delimiter">'</span><span class="content">idseparator</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">-</span><span class="delimiter">'</span></span>,
            <span class="string"><span class="delimiter">'</span><span class="content">docinfo1</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>,
            <span class="string"><span class="delimiter">'</span><span class="content">revealjs_theme</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">black</span><span class="delimiter">'</span></span>,
            <span class="string"><span class="delimiter">'</span><span class="content">revealjs_progress</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">true</span><span class="delimiter">'</span></span>,
            <span class="string"><span class="delimiter">'</span><span class="content">revealjs_touch</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">true</span><span class="delimiter">'</span></span>,
            <span class="string"><span class="delimiter">'</span><span class="content">revealjs_hideAddressBar</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">true</span><span class="delimiter">'</span></span>,
            <span class="string"><span class="delimiter">'</span><span class="content">revealjs_transition</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">linear</span><span class="delimiter">'</span></span>,
            <span class="string"><span class="delimiter">'</span><span class="content">revealjs_history</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">true</span><span class="delimiter">'</span></span>,
            <span class="string"><span class="delimiter">'</span><span class="content">revealjs_slideNumber</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">true</span><span class="delimiter">'</span></span>
    )
    options template_dirs : [<span class="keyword">new</span> <span class="predefined-type">File</span>(<span class="keyword">new</span> <span class="predefined-type">File</span> (projectDir,<span class="string"><span class="delimiter">'</span><span class="content">/resources/asciidoctor-reveal.js</span><span class="delimiter">'</span></span>),<span class="string"><span class="delimiter">'</span><span class="content">templates/slim</span><span class="delimiter">'</span></span>).absolutePath ]

    onlyIf {
        !sourceFiles.findAll {
            <span class="string"><span class="delimiter">'</span><span class="content">revealjs</span><span class="delimiter">'</span></span> <span class="keyword">in</span> <span class="local-variable">it</span>.formats
        }.empty
    }

    sources {
        sourceFiles.findAll {
            <span class="string"><span class="delimiter">'</span><span class="content">revealjs</span><span class="delimiter">'</span></span> <span class="keyword">in</span> <span class="local-variable">it</span>.formats
        }.each {
            include <span class="local-variable">it</span>.file
        }

    }

    outputDir = file(targetDir+<span class="string"><span class="delimiter">'</span><span class="content">/decks/</span><span class="delimiter">'</span></span>)

    resources {
        from(<span class="string"><span class="delimiter">'</span><span class="content">resources</span><span class="delimiter">'</span></span>) {
            include <span class="string"><span class="delimiter">'</span><span class="content">reveal.js/**</span><span class="delimiter">'</span></span>
        }
        from(sourceDir) {
            include <span class="string"><span class="delimiter">'</span><span class="content">images/**</span><span class="delimiter">'</span></span>
        }
        into(<span class="string"><span class="delimiter">&quot;</span><span class="content">ppt</span><span class="delimiter">&quot;</span></span>)
        logger.info <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">${</span>docDir<span class="inline-delimiter">}</span></span><span class="content">/</span><span class="inline"><span class="inline-delimiter">${</span>config.outputPath<span class="inline-delimiter">}</span></span><span class="content">/ppt/images</span><span class="delimiter">&quot;</span></span>
    }
}</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect2">
<h3 id="_publishtoconfluence">3.9. publishToConfluence</h3>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_publishToConfluence.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_publishToConfluence.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/aacd09e76c0affa36f76f8ef337a1536?d=identicon" alt="Luis Muniz " width="32" title="Luis Muniz "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/3c015f3ee9960d842c195993cbb91cff?d=identicon" alt="Tim Riemer " width="32" title="Tim Riemer "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/be47969496f94a5a89159d70cd82bda0?d=identicon" alt="sabatmonk " width="32" title="sabatmonk "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/4280fc350b2b3cb323d151be608be3f8?d=identicon" alt="Alexander Herrmann " width="32" title="Alexander Herrmann "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5f528a3f6814d28b583f31842e3e8d9e?d=identicon" alt="Alexander Schwartz " width="32" title="Alexander Schwartz "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/6c384e5d2b684c60fc8f062d75dae3f7?d=identicon" alt="Alfon " width="32" title="Alfon "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/73252f762a6ec69bb59a23c8ff861c4a?d=identicon" alt="David M. Carr " width="32" title="David M. Carr "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/6c5a117679f7101b5de26dd25a2a8e98?d=identicon" alt="Dierk Höppner " width="32" title="Dierk Höppner "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/69124393772349d32fb9fe975545757d?d=identicon" alt="Lars Francke " width="32" title="Lars Francke "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/415bf2233be007b088ca81b749f02071?d=identicon" alt="Nils Mahlstaedt " width="32" title="Nils Mahlstaedt "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/2dc2929d0609d6338d2794446156f312?d=identicon" alt="Thomas Grabietz " width="32" title="Thomas Grabietz "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/e7d9e6618a36e94b35d43cbd2925aee0?d=identicon" alt="bit-jkraushaar " width="32" title="bit-jkraushaar "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/7107bc30257a84fd371a188bda07309f?d=identicon" alt="oliver drewes " width="32" title="oliver drewes "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>5 minutes to read</pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="images/ea/Manual/publishToConfluence.png" alt="publishToConfluence">
</div>
</div>
<div class="paragraph">
<p>This target takes the generated HTML file, splits it by headline and pushes it to Confluence.
This enables you to use the docs-as-code approach while getting feedback from non-techies through Confluence comments.
And it fulfills the "requirement" of "&#8230;&#8203; but all documentation is in Confluence".</p>
</div>
<div class="paragraph">
<p>Special features:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>[source]</code>-blocks are converted to code-macro blocks in confluence.</p>
</li>
<li>
<p>only pages and images which have changed between task runs are really published and hence only for those changes notifications are sent to watchers. This is quite important - otherwise watchers are easily annoyed by too many notifications.</p>
</li>
<li>
<p><code>:keywords:</code> Keywords are attached as labels to every generated Confluence page. The rules for page labels should be kept in mind. See <a href="https://confluence.atlassian.com/doc/add-remove-and-search-for-labels-136419.html" class="bare">https://confluence.atlassian.com/doc/add-remove-and-search-for-labels-136419.html</a>. Several keywords are allowed. They must be separated by comma, e.g. <code>:keywords: label_1, label-2, label3, &#8230;&#8203;</code>. Labels (keywords) must not contain a space character! Use '_' or '-'.</p>
</li>
</ul>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
code-macro-blocks in confluence render an error if the language attribute contains an unknown language.
See <a href="https://confluence.atlassian.com/doc/code-block-macro-139390.html" class="bare">https://confluence.atlassian.com/doc/code-block-macro-139390.html</a> for a list of valid language and how to add further languages.
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_configuration">3.9.1. Configuration</h4>
<div class="paragraph">
<p>We tried to make the configuration self-explaining, but there are always some note to add.</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">ancestorId</dt>
<dd>
<p>this is the page ID of the parent page to which you want your docs to be published.
Go to this page, click on edit and the needed ID will show up in the URL.
Specify the ID as string within the config file.</p>
</dd>
<dt class="hdlist1">api</dt>
<dd>
<p>for cloud instances, <code>[context]</code> is <code>wiki</code></p>
</dd>
<dt class="hdlist1">preambleTitle</dt>
<dd>
<p>the title of the page containing the preamble (everything the first second level heading). Default is 'arc42'</p>
</dd>
<dt class="hdlist1">disableToC</dt>
<dd>
<p>This boolean configuration define if the Table of Content (ToC) is disabled from the page once uploaded in confluence. (it is false by default, so the ToC is active)</p>
</dd>
<dt class="hdlist1">pagePrefix/pageSuffix</dt>
<dd>
<p>Confluence can&#8217;t handle two pages with the same name.
Moreover, the script matches pages regardless of the case. It will thus refuse to replace a page whose title only differs in case with an existing page.
So you should create a new confluence space for each piece of larger documentation.
If you are restricted and can&#8217;t create new spaces, you can use this <code>pagePrefix</code>/<code>pageSuffix</code> to define a prefix/suffix for this doc so that it doesn&#8217;t conflict with other page names.</p>
</dd>
<dt class="hdlist1">credentials</dt>
<dd>
<p>Use username and password or even better username and api-token.
You can create new API-tokens in <a href="https://confluence.atlassian.com/cloud/api-tokens-938839638.html">your profile</a>.
To avoid having your password or api-token versioned through git, you can store it outside of this configuration as environment variable or in another file - the key here is that the config file is a groovy script.
e.g. you can do things like <code>credentials = "user:${new File("/home/me/apitoken").text}".bytes.encodeBase64().toString()</code></p>
</dd>
</dl>
</div>
<div class="paragraph">
<p>To simplify the injection of credentials from external sources there is a fallback. Should you leave the credentials field empty,
the variables <code>confluenceUser</code> and <code>confluencePassword</code> from the build environment will be used for authentication. You can set these through any means
allowed by gradle like the <code>gradle.properties</code> file in the project or your home directory, environment variables or command-line flags.
For all ways to set these variables, have a look at the <a href="https://docs.gradle.org/current/userguide/build_environment.html">gradle manual</a>.</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">extraPageContent</dt>
<dd>
<p>If you need to prefix your pages with a warning that this is generated content - this is the right place.</p>
</dd>
<dt class="hdlist1">enableAttachments</dt>
<dd>
<p>If value is set to <strong>true</strong>, your links to local file references will be uploaded as attachments. The current implementation only supports a single folder. This foldername will be used as a prefix to validate if your file should be uploaded or not.
In case you enable this feature, and use a folder which starts with "attachment*", an adaption of this prefix is required.</p>
</dd>
</dl>
</div>
<div class="paragraph">
<p>Example:</p>
</div>
<div class="paragraph">
<p>All files to attach will require to be linked inside the document.</p>
</div>
<div class="paragraph">
<p><code><a href="attachement/myfolder/myfile.json">My API definition</a></code></p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">attachmentPrefix</dt>
<dd>
<p>The expected foldername of your output dir. <strong>Default</strong>: <code>attachment</code></p>
</dd>
<dt class="hdlist1">proxy</dt>
<dd>
<p>If you need to provide a proxy to access Confluence, you may set a map with keys <code>host</code> (e.g. <code>'my.proxy.com'</code>), <code>port</code> (e.g. <code>'1234'</code>) and <code>schema</code> (e.g. <code>'http'</code>) of your proxy.</p>
</dd>
<dt class="hdlist1">useOpenapiMacro</dt>
<dd>
<p>If this option is present and equal to <code>true</code>, then any source block marked with class openapi will be wrapped in a swagger-editor macro.
(see <a href="https://marketplace.atlassian.com/apps/1218914/open-api-swagger-editor-for-confluence">Elitesoft Swagger Editor</a>, based on swagger-ui)<br>
This is how you&#8217;d include your openapi YAML file:<br></p>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="asciidoc">[source.openapi,yaml]
----
include::myopeanapi.yaml[]
----</code></pre>
</div>
</div>
</dd>
</dl>
</div>
<div class="listingblock">
<div class="title">publishToConfluence.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy"><span class="comment">//Configureation for publishToConfluence</span>

confluence = [:]

<span class="comment">// 'input' is an array of files to upload to Confluence with the ability</span>
<span class="comment">//          to configure a different parent page for each file.</span>
<span class="comment">//</span>
<span class="comment">// Attributes</span>
<span class="comment">// - 'file': absolute or relative path to the asciidoc generated html file to be exported</span>
<span class="comment">// - 'url': absolute URL to an asciidoc generated html file to be exported</span>
<span class="comment">// - 'ancestorId' (optional): the id of the parent page in Confluence as string; leave this empty</span>
<span class="comment">//                            if a new parent shall be created in the space</span>
<span class="comment">// - 'preambleTitle' (optional): the title of the page containing the preamble (everything</span>
<span class="comment">//                            before the first second level heading). Default is 'arc42'</span>
<span class="comment">//</span>
<span class="comment">// The following four keys can also be used in the global section below</span>
<span class="comment">// - 'spaceKey' (optional): page specific variable for the key of the confluence space to write to</span>
<span class="comment">// - 'createSubpages' (optional): page specific variable to determine whether &quot;.sect2&quot; sections shall be split from the current page into subpages</span>
<span class="comment">// - 'pagePrefix' (optional): page specific variable, the pagePrefix will be a prefix for the page title and it's sub-pages</span>
<span class="comment">//                            use this if you only have access to one confluence space but need to store several</span>
<span class="comment">//                            pages with the same title - a different pagePrefix will make them unique</span>
<span class="comment">// - 'pageSuffix' (optional): same usage as prefix but appended to the title and it's subpages</span>
<span class="comment">// only 'file' or 'url' is allowed. If both are given, 'url' is ignored</span>
confluence.with {
    input = [
            [ <span class="key">file</span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">build/docs/html5/arc42-template-de.html</span><span class="delimiter">&quot;</span></span> ],
    ]

    <span class="comment">// endpoint of the confluenceAPI (REST) to be used</span>
    api = <span class="string"><span class="delimiter">'</span><span class="content">https://[yourServer]/[context]/rest/api/</span><span class="delimiter">'</span></span>

<span class="comment">//    Additionally, spaceKey, createSubpages, pagePrefix and pageSuffix can be globally defined here. The assignment in the input array has precedence</span>

    <span class="comment">// the key of the confluence space to write to</span>
    spaceKey = <span class="string"><span class="delimiter">'</span><span class="content">asciidoc</span><span class="delimiter">'</span></span>

    <span class="comment">// the title of the page containing the preamble (everything the first second level heading). Default is 'arc42'</span>
    preambleTitle = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>

    <span class="comment">// variable to determine whether &quot;.sect2&quot; sections shall be split from the current page into subpages</span>
    createSubpages = <span class="predefined-constant">false</span>

    <span class="comment">// the pagePrefix will be a prefix for each page title</span>
    <span class="comment">// use this if you only have access to one confluence space but need to store several</span>
    <span class="comment">// pages with the same title - a different pagePrefix will make them unique</span>
    pagePrefix = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>

    pageSuffix = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>

    <span class="comment">// username:password of an account which has the right permissions to create and edit</span>
    <span class="comment">// confluence pages in the given space.</span>
    <span class="comment">// if you want to store it securely, fetch it from some external storage or leave it empty to fallback to gradle variables</span>
    <span class="comment">// set through gradle properties files or environment variables. The fallback uses the 'confluenceUser' and 'confluencePassword' keys.</span>
    <span class="comment">// you might even want to prompt the user for the password like in this example</span>

    credentials = <span class="string"><span class="delimiter">&quot;</span><span class="content">user:pass_or_token</span><span class="delimiter">&quot;</span></span>.bytes.encodeBase64().toString()

    <span class="comment">// HTML Content that will be included with every page published</span>
    <span class="comment">// directly after the TOC. If left empty no additional content will be</span>
    <span class="comment">// added</span>
    <span class="comment">// extraPageContent = '&lt;ac:structured-macro ac:name=&quot;warning&quot;&gt;&lt;ac:parameter ac:name=&quot;title&quot; /&gt;&lt;ac:rich-text-body&gt;This is a generated page, do not edit!&lt;/ac:rich-text-body&gt;&lt;/ac:structured-macro&gt;</span>
    extraPageContent = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>

    <span class="comment">// enable or disable attachment uploads for local file references</span>
    enableAttachments = <span class="predefined-constant">false</span>

    <span class="comment">// default attachmentPrefix = attachment - All files to attach will require to be linked inside the document.</span>
    <span class="comment">// attachmentPrefix = &quot;attachment&quot;</span>


    <span class="comment">// Optional proxy configuration, only used to access Confluence</span>
    <span class="comment">// schema supports http and https</span>
    <span class="comment">// proxy = [host: 'my.proxy.com', port: 1234, schema: 'http']</span>
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_css_styling">3.9.2. CSS Styling</h4>
<div class="paragraph">
<p>Some AsciiDoctor features depend on particular CSS style definitions.
Unless these styles are defined, some formatting that is present in the HTML version will not be represented when published to Confluence.</p>
</div>
<div class="olist arabic">
<div class="title">To configure Confluence to include additional style definitions:</div>
<ol class="arabic">
<li>
<p>Log in to Confluence as a space admin</p>
</li>
<li>
<p>Go to the desired space</p>
</li>
<li>
<p>Select "Space tools", "Look and Feel", "Stylesheet"</p>
</li>
<li>
<p>Click "Edit", enter the desired style definitions, and click "Save"</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>The default style definitions can be found in the AsciiDoc project as <a href="https://raw.githubusercontent.com/asciidoctor/asciidoctor/master/data/stylesheets/asciidoctor-default.css">asciidoctor-default.css</a>.
Note that you likely do <strong>NOT</strong> want to include the whole thing, as some of the definitions are likely to disrupt Confluence&#8217;s layout.</p>
</div>
<div class="paragraph">
<p>The following style definitions are believed to be Confluence-compatible, and enable use of the built-in roles (<code>big</code>/<code>small</code>, <code>underline</code>/<code>overline</code>/<code>line-through</code>, <code><em>COLOR</em></code>/<code><em>COLOR</em>-background</code> for the <a href="https://en.wikipedia.org/wiki/Web_colors#HTML_color_names">sixteen HTML color names</a>):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="css"><span class="class">.big</span>{<span class="key">font-size</span>:<span class="value">larger</span>}
<span class="class">.small</span>{<span class="key">font-size</span>:<span class="value">smaller</span>}
<span class="class">.underline</span>{<span class="key">text-decoration</span>:<span class="value">underline</span>}
<span class="class">.overline</span>{<span class="key">text-decoration</span>:<span class="value">overline</span>}
<span class="class">.line-through</span>{<span class="key">text-decoration</span>:<span class="value">line-through</span>}
<span class="class">.aqua</span>{<span class="key">color</span>:<span class="color">#00bfbf</span>}
<span class="class">.aqua-background</span>{<span class="key">background-color</span>:<span class="color">#00fafa</span>}
<span class="class">.black</span>{<span class="key">color</span>:<span class="color">#000</span>}
<span class="class">.black-background</span>{<span class="key">background-color</span>:<span class="color">#000</span>}
<span class="class">.blue</span>{<span class="key">color</span>:<span class="color">#0000bf</span>}
<span class="class">.blue-background</span>{<span class="key">background-color</span>:<span class="color">#0000fa</span>}
<span class="class">.fuchsia</span>{<span class="key">color</span>:<span class="color">#bf00bf</span>}
<span class="class">.fuchsia-background</span>{<span class="key">background-color</span>:<span class="color">#fa00fa</span>}
<span class="class">.gray</span>{<span class="key">color</span>:<span class="color">#606060</span>}
<span class="class">.gray-background</span>{<span class="key">background-color</span>:<span class="color">#7d7d7d</span>}
<span class="class">.green</span>{<span class="key">color</span>:<span class="color">#006000</span>}
<span class="class">.green-background</span>{<span class="key">background-color</span>:<span class="color">#007d00</span>}
<span class="class">.lime</span>{<span class="key">color</span>:<span class="color">#00bf00</span>}
<span class="class">.lime-background</span>{<span class="key">background-color</span>:<span class="color">#00fa00</span>}
<span class="class">.maroon</span>{<span class="key">color</span>:<span class="color">#600000</span>}
<span class="class">.maroon-background</span>{<span class="key">background-color</span>:<span class="color">#7d0000</span>}
<span class="class">.navy</span>{<span class="key">color</span>:<span class="color">#000060</span>}
<span class="class">.navy-background</span>{<span class="key">background-color</span>:<span class="color">#00007d</span>}
<span class="class">.olive</span>{<span class="key">color</span>:<span class="color">#606000</span>}
<span class="class">.olive-background</span>{<span class="key">background-color</span>:<span class="color">#7d7d00</span>}
<span class="class">.purple</span>{<span class="key">color</span>:<span class="color">#600060</span>}
<span class="class">.purple-background</span>{<span class="key">background-color</span>:<span class="color">#7d007d</span>}
<span class="class">.red</span>{<span class="key">color</span>:<span class="color">#bf0000</span>}
<span class="class">.red-background</span>{<span class="key">background-color</span>:<span class="color">#fa0000</span>}
<span class="class">.silver</span>{<span class="key">color</span>:<span class="color">#909090</span>}
<span class="class">.silver-background</span>{<span class="key">background-color</span>:<span class="color">#bcbcbc</span>}
<span class="class">.teal</span>{<span class="key">color</span>:<span class="color">#006060</span>}
<span class="class">.teal-background</span>{<span class="key">background-color</span>:<span class="color">#007d7d</span>}
<span class="class">.white</span>{<span class="key">color</span>:<span class="color">#bfbfbf</span>}
<span class="class">.white-background</span>{<span class="key">background-color</span>:<span class="color">#fafafa</span>}
<span class="class">.yellow</span>{<span class="key">color</span>:<span class="color">#bfbf00</span>}
<span class="class">.yellow-background</span>{<span class="key">background-color</span>:<span class="color">#fafa00</span>}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_source_5">3.9.3. Source</h4>
<div class="listingblock">
<div class="title">publishToConfluence.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">task publishToConfluence(
        <span class="key">description</span>: <span class="string"><span class="delimiter">'</span><span class="content">publishes the HTML rendered output to confluence</span><span class="delimiter">'</span></span>,
        <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">docToolchain</span><span class="delimiter">'</span></span>
) {
    doLast {
        logger.info(<span class="string"><span class="delimiter">&quot;</span><span class="content">docToolchain&gt; docDir: </span><span class="delimiter">&quot;</span></span>+docDir)
        <span class="keyword">def</span> configFile = <span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, mainConfigFile)
        <span class="keyword">def</span> config = <span class="keyword">new</span> ConfigSlurper().parse(configFile.text)

        <span class="keyword">if</span>(config.confluence.credentials.isEmpty()){
            logger.quiet(<span class="string"><span class="delimiter">&quot;</span><span class="content">no credentials set in '</span><span class="inline"><span class="inline-delimiter">${</span>configFile.name<span class="inline-delimiter">}</span></span><span class="content">'</span><span class="delimiter">&quot;</span></span>)
            logger.quiet(<span class="string"><span class="delimiter">&quot;</span><span class="content">trying 'confluenceUser' and 'confluencePassword' from gradle properties</span><span class="delimiter">&quot;</span></span>)

            <span class="keyword">if</span>(project.hasProperty(<span class="string"><span class="delimiter">&quot;</span><span class="content">confluenceUser</span><span class="delimiter">&quot;</span></span>) &amp;&amp; project.hasProperty(<span class="string"><span class="delimiter">&quot;</span><span class="content">confluencePassword</span><span class="delimiter">&quot;</span></span>)){
                config.confluence.credentials = <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">${</span>confluenceUser<span class="inline-delimiter">}</span></span><span class="content">:</span><span class="inline"><span class="inline-delimiter">${</span>confluencePassword<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>.bytes.encodeBase64().toString()
            }<span class="keyword">else</span>{
                logger.quiet(<span class="string"><span class="delimiter">&quot;</span><span class="content">variables 'confluenceUser' and/or 'confluencePassword' are not set!</span><span class="delimiter">&quot;</span></span>)
                logger.quiet(<span class="string"><span class="delimiter">&quot;</span><span class="content">please specify them through gradle.properties, the commandline or environment variables</span><span class="delimiter">&quot;</span></span>)
            }

        }

        binding.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">config</span><span class="delimiter">'</span></span>,config)
        binding.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">docDir</span><span class="delimiter">'</span></span>,docDir)
        binding.setProperty(<span class="string"><span class="delimiter">'</span><span class="content">configFile</span><span class="delimiter">'</span></span>, configFile)
        evaluate(<span class="keyword">new</span> <span class="predefined-type">File</span>(projectDir, <span class="string"><span class="delimiter">'</span><span class="content">scripts/asciidoc2confluence.groovy</span><span class="delimiter">'</span></span>))
    }
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">scripts/asciidoc2confluence.groovy</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy"><span class="comment">/**
 * Created by Ralf D. Mueller and Alexander Heusingfeld
 * https://github.com/rdmueller/asciidoc2confluence
 *
 * this script expects an HTML document created with AsciiDoctor
 * in the following style (default AsciiDoctor output)
 * &lt;div class=&quot;sect1&quot;&gt;
 *     &lt;h2&gt;Page Title&lt;/h2&gt;
 *     &lt;div class=&quot;sectionbody&quot;&gt;
 *         &lt;div class=&quot;sect2&quot;&gt;
 *            &lt;h3&gt;Sub-Page Title&lt;/h3&gt;
 *         &lt;/div&gt;
 *         &lt;div class=&quot;sect2&quot;&gt;
 *            &lt;h3&gt;Sub-Page Title&lt;/h3&gt;
 *         &lt;/div&gt;
 *     &lt;/div&gt;
 * &lt;/div&gt;
 * &lt;div class=&quot;sect1&quot;&gt;
 *     &lt;h2&gt;Page Title&lt;/h2&gt;
 *     ...
 * &lt;/div&gt;
 *
 */</span>

<span class="comment">/*
    Additions for issue #342 marked as #342-dierk42
    ;-)
*/</span>

<span class="comment">// some dependencies</span>
<span class="comment">/**
@Grapes(
        [@Grab('org.jsoup:jsoup:1.8.2'),
         @Grab('org.codehaus.groovy.modules.http-builder:http-builder:0.6' ),
         @Grab('org.apache.httpcomponents:httpmime:4.5.1')]
)
**/</span>
<span class="keyword">import</span> <span class="include">org.jsoup.Jsoup</span>
<span class="keyword">import</span> <span class="include">org.jsoup.parser.Parser</span>
<span class="keyword">import</span> <span class="include">org.jsoup.nodes.Entities.EscapeMode</span>
<span class="keyword">import</span> <span class="include">org.jsoup.nodes.Document</span>
<span class="keyword">import</span> <span class="include">org.jsoup.nodes.Document.OutputSettings</span>
<span class="keyword">import</span> <span class="include">org.jsoup.nodes.Element</span>
<span class="keyword">import</span> <span class="include">org.jsoup.nodes.TextNode</span>
<span class="keyword">import</span> <span class="include">org.jsoup.select.Elements</span>
<span class="keyword">import</span> <span class="include">groovyx.net.http.RESTClient</span>
<span class="keyword">import</span> <span class="include">groovyx.net.http.HttpResponseException</span>
<span class="keyword">import</span> <span class="include">groovyx.net.http.HTTPBuilder</span>
<span class="keyword">import</span> <span class="include">groovyx.net.http.EncoderRegistry</span>
<span class="keyword">import</span> <span class="include">groovyx.net.http.ContentType</span>
<span class="keyword">import</span> <span class="include">java.security.MessageDigest</span>
<span class="comment">//to upload attachments:</span>
<span class="keyword">import</span> <span class="include">org.apache.http.entity.mime.MultipartEntity</span>
<span class="keyword">import</span> <span class="include">org.apache.http.entity.mime.content.StringBody</span>
<span class="keyword">import</span> <span class="include">org.apache.http.entity.mime.content.InputStreamBody</span>
<span class="keyword">import</span> <span class="include">org.apache.http.entity.mime.HttpMultipartMode</span>
<span class="keyword">import</span> <span class="include">groovyx.net.http.Method</span>

<span class="keyword">def</span> CDATA_PLACEHOLDER_START = <span class="string"><span class="delimiter">'</span><span class="content">&lt;cdata-placeholder&gt;</span><span class="delimiter">'</span></span>
<span class="keyword">def</span> CDATA_PLACEHOLDER_END = <span class="string"><span class="delimiter">'</span><span class="content">&lt;/cdata-placeholder&gt;</span><span class="delimiter">'</span></span>

<span class="keyword">def</span> baseUrl
<span class="keyword">def</span> allPages
<span class="comment">// configuration</span>

<span class="keyword">def</span> confluenceSpaceKey
<span class="keyword">def</span> confluenceCreateSubpages
<span class="keyword">def</span> confluencePagePrefix
<span class="keyword">def</span> baseApiPath = <span class="keyword">new</span> <span class="predefined-type">URI</span>(config.confluence.api).path
<span class="comment">// helper functions</span>

<span class="keyword">def</span> <span class="function">MD5</span>(<span class="predefined-type">String</span> s) {
    <span class="predefined-type">MessageDigest</span>.getInstance(<span class="string"><span class="delimiter">&quot;</span><span class="content">MD5</span><span class="delimiter">&quot;</span></span>).digest(s.bytes).encodeHex().toString()
}

<span class="comment">// for getting better error message from the REST-API</span>
<span class="comment">// LuisMuniz: return the action's result, if successful.</span>
<span class="keyword">def</span> <span class="function">trythis</span>(Closure action) {
    <span class="keyword">try</span> {
        action.call()
    } <span class="keyword">catch</span> (HttpResponseException error) {
        println <span class="string"><span class="delimiter">&quot;</span><span class="content">something went wrong - got an http response code </span><span class="delimiter">&quot;</span></span>+error.response.status+<span class="string"><span class="delimiter">&quot;</span><span class="content">:</span><span class="delimiter">&quot;</span></span>
        <span class="keyword">switch</span> (error.response.status) {
            <span class="keyword">case</span> <span class="string"><span class="delimiter">'</span><span class="content">401</span><span class="delimiter">'</span></span>:
                println (error.response.data.toString().replaceAll(<span class="string"><span class="delimiter">&quot;</span><span class="content">^.*Reason</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">Reason</span><span class="delimiter">&quot;</span></span>))
                println <span class="string"><span class="delimiter">&quot;</span><span class="content">please check your confluence credentials in </span><span class="delimiter">&quot;</span></span>+configFile.canonicalPath
                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="exception">Exception</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">missing authentication credentials</span><span class="delimiter">&quot;</span></span>)
                <span class="keyword">break</span>
            <span class="keyword">default</span>:
                println error.response.data
        }
        <span class="predefined-constant">null</span>
        <span class="comment">//throw error</span>
    }
}


<span class="keyword">def</span> <span class="function">parseAdmonitionBlock</span>(block, <span class="predefined-type">String</span> type) {
    content = block.select(<span class="string"><span class="delimiter">&quot;</span><span class="content">.content</span><span class="delimiter">&quot;</span></span>).first()
    titleElement = content.select(<span class="string"><span class="delimiter">&quot;</span><span class="content">.title</span><span class="delimiter">&quot;</span></span>)
    titleText = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
    <span class="keyword">if</span>(titleElement != <span class="predefined-constant">null</span>) {
        titleText = <span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;ac:parameter ac:name=</span><span class="char">\&quot;</span><span class="content">title</span><span class="char">\&quot;</span><span class="content">&gt;</span><span class="inline"><span class="inline-delimiter">${</span>titleElement.text()<span class="inline-delimiter">}</span></span><span class="content">&lt;/ac:parameter&gt;</span><span class="delimiter">&quot;</span></span>
        titleElement.remove()
    }
    block.after(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;ac:structured-macro ac:name=</span><span class="char">\&quot;</span><span class="inline"><span class="inline-delimiter">${</span>type<span class="inline-delimiter">}</span></span><span class="char">\&quot;</span><span class="content">&gt;</span><span class="inline"><span class="inline-delimiter">${</span>titleText<span class="inline-delimiter">}</span></span><span class="content">&lt;ac:rich-text-body&gt;</span><span class="inline"><span class="inline-delimiter">${</span>content<span class="inline-delimiter">}</span></span><span class="content">&lt;/ac:rich-text-body&gt;&lt;/ac:structured-macro&gt;</span><span class="delimiter">&quot;</span></span>)
    block.remove()
}

<span class="comment">/*  #342-dierk42

    add labels to a Confluence page. Labels are taken from :keywords: which
    are converted as meta tags in HTML. Building the array: see below

    Confluence allows adding labels only after creation of a page.
    Therefore we need extra API calls.

    Currently the labels are added one by one. Suggestion for improvement:
    Build a label structure of all labels an place them with one call.

    Replaces exisiting labels. No harm
    Does not check for deleted labels when keywords are deleted from source
    document!
*/</span>
<span class="keyword">def</span> addLabels = { <span class="keyword">def</span> pageId, <span class="keyword">def</span> labelsArray -&gt;
    <span class="comment">//https://docs.atlassian.com/confluence/REST/latest/</span>
    <span class="keyword">def</span> api = <span class="keyword">new</span> RESTClient(config.confluence.api)
    <span class="comment">//this fixes the encoding (dierk42: Is this needed here? Don't know)</span>
    api.encoderRegistry = <span class="keyword">new</span> EncoderRegistry( <span class="key">charset</span>: <span class="string"><span class="delimiter">'</span><span class="content">utf-8</span><span class="delimiter">'</span></span> )

    <span class="keyword">def</span> headers = [
            <span class="string"><span class="delimiter">'</span><span class="content">Authorization</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">Basic </span><span class="delimiter">'</span></span> + config.confluence.credentials,
            <span class="string"><span class="delimiter">'</span><span class="content">X-Atlassian-Token</span><span class="delimiter">'</span></span>:<span class="string"><span class="delimiter">'</span><span class="content">no-check</span><span class="delimiter">'</span></span>
    ]
    <span class="comment">// Attach each label in a API call of its own. The only prefix possible</span>
    <span class="comment">// in our own Confluence is 'global'</span>
    labelsArray.each { label -&gt;
        label_data = [
            prefix : <span class="string"><span class="delimiter">'</span><span class="content">global</span><span class="delimiter">'</span></span>,
            name : label
        ]
        trythis {
            <span class="comment">// attach label to page pageId</span>
            <span class="comment">// https://developer.atlassian.com/display/CONFDEV/Confluence+REST+API+Examples#ConfluenceRESTAPIExamples-Updatingapage</span>
            <span class="keyword">def</span> res = api.post(<span class="key">contentType</span>: ContentType.JSON,
                              <span class="key">path</span>: <span class="string"><span class="delimiter">'</span><span class="content">content/</span><span class="delimiter">'</span></span> + pageId + <span class="string"><span class="delimiter">&quot;</span><span class="content">/label</span><span class="delimiter">&quot;</span></span>, <span class="key">body</span>: label_data, <span class="key">headers</span>: headers)
            }
        println <span class="string"><span class="delimiter">&quot;</span><span class="content">added label </span><span class="delimiter">&quot;</span></span> + label + <span class="string"><span class="delimiter">&quot;</span><span class="content"> to page ID </span><span class="delimiter">&quot;</span></span> + pageId
    }
}


<span class="keyword">def</span> uploadAttachment = { <span class="keyword">def</span> pageId, <span class="predefined-type">String</span> url, <span class="predefined-type">String</span> fileName, <span class="predefined-type">String</span> note -&gt;
    <span class="keyword">def</span> is
    <span class="keyword">def</span> localHash
    <span class="keyword">if</span> (url.startsWith(<span class="string"><span class="delimiter">'</span><span class="content">http</span><span class="delimiter">'</span></span>)) {
        is = <span class="keyword">new</span> <span class="predefined-type">URL</span>(url).openStream()
        <span class="comment">//build a hash of the attachment</span>
        localHash = MD5(<span class="keyword">new</span> <span class="predefined-type">URL</span>(url).openStream().text)
    } <span class="keyword">else</span> {
        is = <span class="keyword">new</span> <span class="predefined-type">File</span>(url).newDataInputStream()
        <span class="comment">//build a hash of the attachment</span>
        localHash = MD5(<span class="keyword">new</span> <span class="predefined-type">File</span>(url).newDataInputStream().text)
    }

    <span class="comment">//https://docs.atlassian.com/confluence/REST/latest/</span>
    <span class="keyword">def</span> api = <span class="keyword">new</span> RESTClient(config.confluence.api)
    <span class="comment">//this fixes the encoding</span>
    api.encoderRegistry = <span class="keyword">new</span> EncoderRegistry( <span class="key">charset</span>: <span class="string"><span class="delimiter">'</span><span class="content">utf-8</span><span class="delimiter">'</span></span> )
    <span class="keyword">if</span> (config.confluence.proxy) {
        api.setProxy(config.confluence.proxy.host, config.confluence.proxy.port, config.confluence.proxy.schema ?: <span class="string"><span class="delimiter">'</span><span class="content">http</span><span class="delimiter">'</span></span>)
    }

    <span class="keyword">def</span> headers = [
            <span class="string"><span class="delimiter">'</span><span class="content">Authorization</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">Basic </span><span class="delimiter">'</span></span> + config.confluence.credentials,
            <span class="string"><span class="delimiter">'</span><span class="content">X-Atlassian-Token</span><span class="delimiter">'</span></span>:<span class="string"><span class="delimiter">'</span><span class="content">no-check</span><span class="delimiter">'</span></span>
    ]
    <span class="comment">//Add api key and value to REST API request header if configured - required for authentification.</span>
    <span class="keyword">if</span> (config.confluence.apikey)
    {
       headers.keyid = config.confluence.apikey
    }
    <span class="comment">//check if attachment already exists</span>
    <span class="keyword">def</span> result = <span class="string"><span class="delimiter">&quot;</span><span class="content">nothing</span><span class="delimiter">&quot;</span></span>
    <span class="keyword">def</span> attachment = api.get(<span class="key">path</span>: <span class="string"><span class="delimiter">'</span><span class="content">content/</span><span class="delimiter">'</span></span> + pageId + <span class="string"><span class="delimiter">'</span><span class="content">/child/attachment</span><span class="delimiter">'</span></span>,
            <span class="key">query</span>: [
                    <span class="string"><span class="delimiter">'</span><span class="content">filename</span><span class="delimiter">'</span></span>: fileName,
            ], <span class="key">headers</span>: headers).data
    <span class="keyword">def</span> http
    <span class="keyword">if</span> (attachment.size==<span class="integer">1</span>) {
        <span class="comment">// attachment exists. need an update?</span>
        <span class="keyword">def</span> remoteHash = attachment.results[<span class="integer">0</span>].extensions.comment.replaceAll(<span class="string"><span class="delimiter">&quot;</span><span class="content">(?sm).*#([^#]+)#.*</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">$1</span><span class="delimiter">'</span></span>)
        <span class="keyword">if</span> (remoteHash!=localHash) {
            <span class="comment">//hash is different -&gt; attachment needs to be updated</span>
            http = <span class="keyword">new</span> HTTPBuilder(config.confluence.api + <span class="string"><span class="delimiter">'</span><span class="content">content/</span><span class="delimiter">'</span></span> + pageId + <span class="string"><span class="delimiter">'</span><span class="content">/child/attachment/</span><span class="delimiter">'</span></span> + attachment.results[<span class="integer">0</span>].id + <span class="string"><span class="delimiter">'</span><span class="content">/data</span><span class="delimiter">'</span></span>)
            println <span class="string"><span class="delimiter">&quot;</span><span class="content">    updated attachment</span><span class="delimiter">&quot;</span></span>
        }
    } <span class="keyword">else</span> {
        http = <span class="keyword">new</span> HTTPBuilder(config.confluence.api + <span class="string"><span class="delimiter">'</span><span class="content">content/</span><span class="delimiter">'</span></span> + pageId + <span class="string"><span class="delimiter">'</span><span class="content">/child/attachment</span><span class="delimiter">'</span></span>)
        <span class="keyword">if</span> (config.confluence.proxy) {
            http.setProxy(config.confluence.proxy.host, config.confluence.proxy.port, config.confluence.proxy.schema ?: <span class="string"><span class="delimiter">'</span><span class="content">http</span><span class="delimiter">'</span></span>)
        }
    }
    <span class="keyword">if</span> (http) {
        http.request(<span class="predefined-type">Method</span>.POST) { req -&gt;
            <span class="key">requestContentType</span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">multipart/form-data</span><span class="delimiter">&quot;</span></span>
            MultipartEntity multiPartContent = <span class="keyword">new</span> MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE)
            <span class="comment">// Adding Multi-part file parameter &quot;file&quot;</span>
            multiPartContent.addPart(<span class="string"><span class="delimiter">&quot;</span><span class="content">file</span><span class="delimiter">&quot;</span></span>, <span class="keyword">new</span> InputStreamBody(is, fileName))
            <span class="comment">// Adding another string parameter &quot;comment&quot;</span>
            multiPartContent.addPart(<span class="string"><span class="delimiter">&quot;</span><span class="content">comment</span><span class="delimiter">&quot;</span></span>, <span class="keyword">new</span> StringBody(note + <span class="string"><span class="delimiter">&quot;</span><span class="char">\r</span><span class="char">\n</span><span class="content">#</span><span class="delimiter">&quot;</span></span> + localHash + <span class="string"><span class="delimiter">&quot;</span><span class="content">#</span><span class="delimiter">&quot;</span></span>))
            req.setEntity(multiPartContent)
            headers.each { key, value -&gt;
                req.addHeader(key, value)
            }
        }
    }
}


<span class="keyword">def</span> realTitle = { pageTitle -&gt;
    confluencePagePrefix + pageTitle + confluencePageSuffix
}

<span class="keyword">def</span> rewriteMarks = { body -&gt;
    <span class="comment">// Confluence strips out mark elements.  Replace them with default formatting.</span>
    body.select(<span class="string"><span class="delimiter">'</span><span class="content">mark</span><span class="delimiter">'</span></span>).wrap(<span class="string"><span class="delimiter">'</span><span class="content">&lt;span style=&quot;background:#ff0;color:#000&quot;&gt;&lt;/style&gt;</span><span class="delimiter">'</span></span>).unwrap()
}

<span class="comment">// #352-LuisMuniz: Helper methods</span>
<span class="comment">// Fetch all pages of the space. Only keep relevant info in the pages Map</span>
<span class="comment">// The map is indexed by lower-case title</span>
<span class="keyword">def</span> retrieveAllPages = { RESTClient api, <span class="predefined-type">Map</span> headers, <span class="predefined-type">String</span> spaceKey -&gt;
    <span class="keyword">if</span> (allPages != <span class="predefined-constant">null</span>) {
        println <span class="string"><span class="delimiter">&quot;</span><span class="content">allPages already retrieved</span><span class="delimiter">&quot;</span></span>
        allPages
    } <span class="keyword">else</span> {

        <span class="type">boolean</span> morePages=<span class="predefined-constant">true</span>
        <span class="type">int</span> start=<span class="integer">0</span>
        <span class="keyword">def</span> request = [
                <span class="string"><span class="delimiter">'</span><span class="content">type</span><span class="delimiter">'</span></span>    : <span class="string"><span class="delimiter">'</span><span class="content">page</span><span class="delimiter">'</span></span>,
                <span class="string"><span class="delimiter">'</span><span class="content">spaceKey</span><span class="delimiter">'</span></span>: spaceKey,
                <span class="string"><span class="delimiter">'</span><span class="content">expand</span><span class="delimiter">'</span></span>  : <span class="string"><span class="delimiter">'</span><span class="content">ancestors</span><span class="delimiter">'</span></span>,
                <span class="string"><span class="delimiter">'</span><span class="content">limit</span><span class="delimiter">'</span></span>   : <span class="integer">100</span>
        ]

        allPages =[:]
        <span class="keyword">while</span>(morePages) {
            <span class="keyword">def</span> results = trythis {
                request.start=start
                <span class="keyword">def</span> args = [
                        <span class="string"><span class="delimiter">'</span><span class="content">headers</span><span class="delimiter">'</span></span>: headers,
                        <span class="string"><span class="delimiter">'</span><span class="content">path</span><span class="delimiter">'</span></span>   : <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">${</span>baseApiPath<span class="inline-delimiter">}</span></span><span class="content">content</span><span class="delimiter">&quot;</span></span>,
                        <span class="string"><span class="delimiter">'</span><span class="content">query</span><span class="delimiter">'</span></span>  : request,
                ]
                api.get(args).data.results
            } ?: <span class="type">[]</span>

            <span class="keyword">if</span> (results.empty) {
                morePages=<span class="predefined-constant">false</span>
            } <span class="keyword">else</span> {
                start += results.size
            }

            results.inject(allPages) { <span class="predefined-type">Map</span> acc, <span class="predefined-type">Map</span> match -&gt;
                <span class="comment">//unique page names in confluence, so we can get away with indexing by title</span>
                <span class="keyword">def</span> ancestors = match.ancestors.collect { <span class="local-variable">it</span>.id }

                acc[match.title.toLowerCase()] = [
                        title   : match.title,
                        id      : match.id,
                        <span class="key">parentId</span>: ancestors.isEmpty() ? <span class="predefined-constant">null</span> : ancestors.last()
                ]
                acc
            }
        }

        allPages
    }
}

<span class="comment">// Retrieve a page by id with contents and version</span>
<span class="keyword">def</span> retrieveFullPage = { RESTClient api, <span class="predefined-type">Map</span> headers, <span class="predefined-type">String</span> id -&gt;
    trythis {
        api.get(
                [
                        <span class="string"><span class="delimiter">'</span><span class="content">headers</span><span class="delimiter">'</span></span>: headers,
                        <span class="string"><span class="delimiter">'</span><span class="content">path</span><span class="delimiter">'</span></span>   : <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">${</span>baseApiPath<span class="inline-delimiter">}</span></span><span class="content">content/</span><span class="inline"><span class="inline-delimiter">${</span>id<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>,
                        <span class="string"><span class="delimiter">'</span><span class="content">query</span><span class="delimiter">'</span></span>  : [<span class="string"><span class="delimiter">'</span><span class="content">expand</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">body.storage,version</span><span class="delimiter">'</span></span>],
                ]
        ).data


    } ?: [:]
}

<span class="comment">//if a parent has been specified, check whether a page has the same parent.</span>
<span class="type">boolean</span> hasRequestedParent(<span class="predefined-type">Map</span> existingPage, <span class="predefined-type">String</span> requestedParentId) {
    <span class="keyword">if</span> (requestedParentId) {
        existingPage.parentId == requestedParentId
    } <span class="keyword">else</span> {
        <span class="predefined-constant">true</span>
    }
}

<span class="keyword">def</span> rewriteDescriptionLists = { body -&gt;
    <span class="keyword">def</span> TAGS = [ <span class="key">dt</span>: <span class="string"><span class="delimiter">'</span><span class="content">th</span><span class="delimiter">'</span></span>, <span class="key">dd</span>: <span class="string"><span class="delimiter">'</span><span class="content">td</span><span class="delimiter">'</span></span> ]
    body.select(<span class="string"><span class="delimiter">'</span><span class="content">dl</span><span class="delimiter">'</span></span>).each { dl -&gt;
        <span class="comment">// WHATWG allows wrapping dt/dd in divs, simply unwrap them</span>
        dl.select(<span class="string"><span class="delimiter">'</span><span class="content">div</span><span class="delimiter">'</span></span>).each { <span class="local-variable">it</span>.unwrap() }

        <span class="comment">// group dts and dds that belong together, usually it will be a 1:1 relation</span>
        <span class="comment">// but HTML allows for different constellations</span>
        <span class="keyword">def</span> rows = <span class="type">[]</span>
        <span class="keyword">def</span> current = [<span class="key">dt</span>: <span class="type">[]</span>, <span class="key">dd</span>: <span class="type">[]</span>]
        rows &lt;&lt; current
        dl.select(<span class="string"><span class="delimiter">'</span><span class="content">dt, dd</span><span class="delimiter">'</span></span>).each { child -&gt;
            <span class="keyword">def</span> tagName = child.tagName()
            <span class="keyword">if</span> (tagName == <span class="string"><span class="delimiter">'</span><span class="content">dt</span><span class="delimiter">'</span></span> &amp;&amp; current.dd.size() &gt; <span class="integer">0</span>) {
                <span class="comment">// dt follows dd, start a new group</span>
                current = [<span class="key">dt</span>: <span class="type">[]</span>, <span class="key">dd</span>: <span class="type">[]</span>]
                rows &lt;&lt; current
            }
            current[tagName] &lt;&lt; child.tagName(TAGS[tagName])
            child.remove()
        }

        rows.each { row -&gt;
            <span class="keyword">def</span> sizes = [<span class="key">dt</span>: row.dt.size(), <span class="key">dd</span>: row.dd.size()]
            <span class="keyword">def</span> rowspanIdx = [<span class="key">dt</span>: -<span class="integer">1</span>, <span class="key">dd</span>: sizes.dd - <span class="integer">1</span>]
            <span class="keyword">def</span> rowspan = <span class="predefined-type">Math</span>.abs(sizes.dt - sizes.dd) + <span class="integer">1</span>
            <span class="keyword">def</span> max = sizes.dt
            <span class="keyword">if</span> (sizes.dt &lt; sizes.dd) {
                max = sizes.dd
                rowspanIdx = [<span class="key">dt</span>: sizes.dt - <span class="integer">1</span>, <span class="key">dd</span>: -<span class="integer">1</span>]
            }
            (<span class="integer">0</span>..&lt;max).each { idx -&gt;
                <span class="keyword">def</span> tr = dl.appendElement(<span class="string"><span class="delimiter">'</span><span class="content">tr</span><span class="delimiter">'</span></span>)
                [<span class="string"><span class="delimiter">'</span><span class="content">dt</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">dd</span><span class="delimiter">'</span></span>].each { type -&gt;
                    <span class="keyword">if</span> (sizes[type] &gt; idx) {
                        tr.appendChild(row[type][idx])
                        <span class="keyword">if</span> (idx == rowspanIdx[type] &amp;&amp; rowspan &gt; <span class="integer">1</span>) {
                            row[type][idx].attr(<span class="string"><span class="delimiter">'</span><span class="content">rowspan</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">${</span>rowspan<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>)
                        }
                    } <span class="keyword">else</span> <span class="keyword">if</span> (idx == <span class="integer">0</span>) {
                        tr.appendElement(TAGS[type]).attr(<span class="string"><span class="delimiter">'</span><span class="content">rowspan</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">${</span>rowspan<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>)
                    }
                }
            }
        }

        dl.wrap(<span class="string"><span class="delimiter">'</span><span class="content">&lt;table&gt;&lt;/table&gt;</span><span class="delimiter">'</span></span>)
            .unwrap()
    }
}

<span class="keyword">def</span> rewriteInternalLinks = { body, anchors, pageAnchors -&gt;
    <span class="comment">// find internal cross-references and replace them with link macros</span>
    body.select(<span class="string"><span class="delimiter">'</span><span class="content">a[href]</span><span class="delimiter">'</span></span>).each { a -&gt;
        <span class="keyword">def</span> href = a.attr(<span class="string"><span class="delimiter">'</span><span class="content">href</span><span class="delimiter">'</span></span>)
        <span class="keyword">if</span> (href.startsWith(<span class="string"><span class="delimiter">'</span><span class="content">#</span><span class="delimiter">'</span></span>)) {
            <span class="keyword">def</span> anchor = href.substring(<span class="integer">1</span>)
            <span class="keyword">def</span> pageTitle = anchors[anchor] ?: pageAnchors[anchor]
            <span class="keyword">if</span> (pageTitle &amp;&amp; a.text()) {
                <span class="comment">// as Confluence insists on link texts to be contained</span>
                <span class="comment">// inside CDATA, we have to strip all HTML and</span>
                <span class="comment">// potentially loose styling that way.</span>
                a.html(a.text())
                a.wrap(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;ac:link</span><span class="inline"><span class="inline-delimiter">${</span>anchors.containsKey(anchor) ? <span class="string"><span class="delimiter">'</span><span class="content"> ac:anchor=&quot;</span><span class="delimiter">'</span></span> + anchor + <span class="string"><span class="delimiter">'</span><span class="content">&quot;</span><span class="delimiter">'</span></span> : <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span><span class="inline-delimiter">}</span></span><span class="content">&gt;&lt;/ac:link&gt;</span><span class="delimiter">&quot;</span></span>)
                   .before(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;ri:page ri:content-title=</span><span class="char">\&quot;</span><span class="inline"><span class="inline-delimiter">${</span>realTitle pageTitle<span class="inline-delimiter">}</span></span><span class="char">\&quot;</span><span class="content">/&gt;</span><span class="delimiter">&quot;</span></span>)
                   .wrap(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;ac:plain-text-link-body&gt;</span><span class="inline"><span class="inline-delimiter">${</span>CDATA_PLACEHOLDER_START<span class="inline-delimiter">}</span></span><span class="inline"><span class="inline-delimiter">${</span>CDATA_PLACEHOLDER_END<span class="inline-delimiter">}</span></span><span class="content">&lt;/ac:plain-text-link-body&gt;</span><span class="delimiter">&quot;</span></span>)
                   .unwrap()
            }
        }
    }
}


<span class="keyword">def</span> rewriteCodeblocks = { body -&gt;
    body.select(<span class="string"><span class="delimiter">'</span><span class="content">pre &gt; code</span><span class="delimiter">'</span></span>).each { code -&gt;
        <span class="keyword">if</span> (code.attr(<span class="string"><span class="delimiter">'</span><span class="content">data-lang</span><span class="delimiter">'</span></span>)) {
            code.select(<span class="string"><span class="delimiter">'</span><span class="content">span[class]</span><span class="delimiter">'</span></span>).each { span -&gt;
                span.unwrap()
            }
            code.select(<span class="string"><span class="delimiter">'</span><span class="content">i[class]</span><span class="delimiter">'</span></span>).each { i -&gt;
                i.unwrap()
            }
            code.select(<span class="string"><span class="delimiter">'</span><span class="content">b</span><span class="delimiter">'</span></span>).each { b -&gt;
                b.before(<span class="string"><span class="delimiter">&quot;</span><span class="content"> // </span><span class="delimiter">&quot;</span></span>)
                b.unwrap()
            }
            code.before(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;ac:parameter ac:name=</span><span class="char">\&quot;</span><span class="content">language</span><span class="char">\&quot;</span><span class="content">&gt;</span><span class="inline"><span class="inline-delimiter">${</span>code.attr(<span class="string"><span class="delimiter">'</span><span class="content">data-lang</span><span class="delimiter">'</span></span>)<span class="inline-delimiter">}</span></span><span class="content">&lt;/ac:parameter&gt;</span><span class="delimiter">&quot;</span></span>)
        }
        code.parent() <span class="comment">// pre now</span>
            .wrap(<span class="string"><span class="delimiter">'</span><span class="content">&lt;ac:structured-macro ac:name=&quot;code&quot;&gt;&lt;/ac:structured-macro&gt;</span><span class="delimiter">'</span></span>)
            .unwrap()
        code.wrap(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;ac:plain-text-body&gt;</span><span class="inline"><span class="inline-delimiter">${</span>CDATA_PLACEHOLDER_START<span class="inline-delimiter">}</span></span><span class="inline"><span class="inline-delimiter">${</span>CDATA_PLACEHOLDER_END<span class="inline-delimiter">}</span></span><span class="content">&lt;/ac:plain-text-body&gt;</span><span class="delimiter">&quot;</span></span>)
            .unwrap()
    }
}

<span class="keyword">def</span> rewriteOpenAPI = { org.jsoup.nodes.Element body -&gt;
    <span class="keyword">if</span> (config.confluence.useOpenapiMacro) {
        body.select(<span class="string"><span class="delimiter">'</span><span class="content">div.openapi  pre &gt; code</span><span class="delimiter">'</span></span>).each { code -&gt;
            <span class="keyword">def</span> parent=code.parent()
            <span class="keyword">def</span> rawYaml=code.wholeText()
            code.parent()
                    .wrap(<span class="string"><span class="delimiter">'</span><span class="content">&lt;ac:structured-macro ac:name=&quot;confluence-open-api&quot; ac:schema-version=&quot;1&quot; ac:macro-id=&quot;1dfde21b-6111-4535-928a-470fa8ae3e7d&quot;&gt;&lt;/ac:structured-macro&gt;</span><span class="delimiter">'</span></span>)
                    .unwrap()
            code.wrap(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;ac:plain-text-body&gt;</span><span class="inline"><span class="inline-delimiter">${</span>CDATA_PLACEHOLDER_START<span class="inline-delimiter">}</span></span><span class="inline"><span class="inline-delimiter">${</span>CDATA_PLACEHOLDER_END<span class="inline-delimiter">}</span></span><span class="content">&lt;/ac:plain-text-body&gt;</span><span class="delimiter">&quot;</span></span>)
                    .replaceWith(<span class="keyword">new</span> TextNode(rawYaml))
        }
    }
}

<span class="keyword">def</span> unescapeCDATASections = { html -&gt;
    <span class="keyword">def</span> start = html.indexOf(CDATA_PLACEHOLDER_START)
    <span class="keyword">while</span> (start &gt; -<span class="integer">1</span>) {
        <span class="keyword">def</span> end = html.indexOf(CDATA_PLACEHOLDER_END, start)
        <span class="keyword">if</span> (end &gt; -<span class="integer">1</span>) {
            <span class="keyword">def</span> prefix = html.substring(<span class="integer">0</span>, start) + CDATA_PLACEHOLDER_START
            <span class="keyword">def</span> suffix = html.substring(end)
            <span class="keyword">def</span> unescaped = html.substring(start + CDATA_PLACEHOLDER_START.length(), end)
                    .replaceAll(<span class="string"><span class="delimiter">'</span><span class="content">&amp;lt;</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">&lt;</span><span class="delimiter">'</span></span>).replaceAll(<span class="string"><span class="delimiter">'</span><span class="content">&amp;gt;</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">&gt;</span><span class="delimiter">'</span></span>)
                    .replaceAll(<span class="string"><span class="delimiter">'</span><span class="content">&amp;amp;</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">&amp;</span><span class="delimiter">'</span></span>)
            html = prefix + unescaped + suffix
        }
        start = html.indexOf(CDATA_PLACEHOLDER_START, start + <span class="integer">1</span>)
    }
    html
}

<span class="comment">//modify local page in order to match the internal confluence storage representation a bit better</span>
<span class="comment">//definition lists are not displayed by confluence, so turn them into tables</span>
<span class="comment">//body can be of type Element or Elements</span>
<span class="keyword">def</span> deferredUpload = <span class="type">[]</span>
<span class="keyword">def</span> parseBody =  { body, anchors, pageAnchors -&gt;
    rewriteOpenAPI body

    body.select(<span class="string"><span class="delimiter">'</span><span class="content">div.paragraph</span><span class="delimiter">'</span></span>).unwrap()
    body.select(<span class="string"><span class="delimiter">'</span><span class="content">div.ulist</span><span class="delimiter">'</span></span>).unwrap()
    body.select(<span class="string"><span class="delimiter">'</span><span class="content">div.sect3</span><span class="delimiter">'</span></span>).unwrap()
    [   <span class="string"><span class="delimiter">'</span><span class="content">note</span><span class="delimiter">'</span></span>:<span class="string"><span class="delimiter">'</span><span class="content">info</span><span class="delimiter">'</span></span>,
        <span class="string"><span class="delimiter">'</span><span class="content">warning</span><span class="delimiter">'</span></span>:<span class="string"><span class="delimiter">'</span><span class="content">warning</span><span class="delimiter">'</span></span>,
        <span class="string"><span class="delimiter">'</span><span class="content">important</span><span class="delimiter">'</span></span>:<span class="string"><span class="delimiter">'</span><span class="content">warning</span><span class="delimiter">'</span></span>,
        <span class="string"><span class="delimiter">'</span><span class="content">caution</span><span class="delimiter">'</span></span>:<span class="string"><span class="delimiter">'</span><span class="content">note</span><span class="delimiter">'</span></span>,
        <span class="string"><span class="delimiter">'</span><span class="content">tip</span><span class="delimiter">'</span></span>:<span class="string"><span class="delimiter">'</span><span class="content">tip</span><span class="delimiter">'</span></span>            ].each { adType, cType -&gt;
        body.select(<span class="string"><span class="delimiter">'</span><span class="content">.admonitionblock.</span><span class="delimiter">'</span></span>+adType).each { block -&gt;
            parseAdmonitionBlock(block, cType)
        }
    }
    <span class="comment">//special for the arc42-template</span>
    body.select(<span class="string"><span class="delimiter">'</span><span class="content">div.arc42help</span><span class="delimiter">'</span></span>).select(<span class="string"><span class="delimiter">'</span><span class="content">.content</span><span class="delimiter">'</span></span>)
            .wrap(<span class="string"><span class="delimiter">'</span><span class="content">&lt;ac:structured-macro ac:name=&quot;expand&quot;&gt;&lt;/ac:structured-macro&gt;</span><span class="delimiter">'</span></span>)
            .wrap(<span class="string"><span class="delimiter">'</span><span class="content">&lt;ac:rich-text-body&gt;&lt;/ac:rich-text-body&gt;</span><span class="delimiter">'</span></span>)
            .wrap(<span class="string"><span class="delimiter">'</span><span class="content">&lt;ac:structured-macro ac:name=&quot;info&quot;&gt;&lt;/ac:structured-macro&gt;</span><span class="delimiter">'</span></span>)
            .before(<span class="string"><span class="delimiter">'</span><span class="content">&lt;ac:parameter ac:name=&quot;title&quot;&gt;arc42&lt;/ac:parameter&gt;</span><span class="delimiter">'</span></span>)
            .wrap(<span class="string"><span class="delimiter">'</span><span class="content">&lt;ac:rich-text-body&gt;&lt;p&gt;&lt;/p&gt;&lt;/ac:rich-text-body&gt;</span><span class="delimiter">'</span></span>)
    body.select(<span class="string"><span class="delimiter">'</span><span class="content">div.arc42help</span><span class="delimiter">'</span></span>).unwrap()
    body.select(<span class="string"><span class="delimiter">'</span><span class="content">div.title</span><span class="delimiter">'</span></span>).wrap(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;strong&gt;&lt;/strong&gt;</span><span class="delimiter">&quot;</span></span>).before(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;br /&gt;</span><span class="delimiter">&quot;</span></span>).wrap(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;div&gt;&lt;/div&gt;</span><span class="delimiter">&quot;</span></span>)
    body.select(<span class="string"><span class="delimiter">'</span><span class="content">div.listingblock</span><span class="delimiter">'</span></span>).wrap(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;p&gt;&lt;/p&gt;</span><span class="delimiter">&quot;</span></span>).unwrap()
    <span class="comment">// see if we can find referenced images and fetch them</span>
    <span class="keyword">new</span> <span class="predefined-type">File</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">tmp/images/.</span><span class="delimiter">&quot;</span></span>).mkdirs()
    <span class="comment">// find images, extract their URLs for later uploading (after we know the pageId) and replace them with this macro:</span>
    <span class="comment">// &lt;ac:image ac:align=&quot;center&quot; ac:width=&quot;500&quot;&gt;</span>
    <span class="comment">// &lt;ri:attachment ri:filename=&quot;deployment-context.png&quot;/&gt;</span>
    <span class="comment">// &lt;/ac:image&gt;</span>
    body.select(<span class="string"><span class="delimiter">'</span><span class="content">img</span><span class="delimiter">'</span></span>).each { img -&gt;
        img.attributes().each { attribute -&gt;
            <span class="comment">//println attribute.dump()</span>
        }
        <span class="keyword">def</span> src = img.attr(<span class="string"><span class="delimiter">'</span><span class="content">src</span><span class="delimiter">'</span></span>)
        <span class="keyword">def</span> imgWidth = img.attr(<span class="string"><span class="delimiter">'</span><span class="content">width</span><span class="delimiter">'</span></span>)?:<span class="integer">500</span>
        <span class="keyword">def</span> imgAlign = img.attr(<span class="string"><span class="delimiter">'</span><span class="content">align</span><span class="delimiter">'</span></span>)?:<span class="string"><span class="delimiter">&quot;</span><span class="content">center</span><span class="delimiter">&quot;</span></span>
        println <span class="string"><span class="delimiter">&quot;</span><span class="content">    image: </span><span class="delimiter">&quot;</span></span>+src

        <span class="comment">//it is not an online image, so upload it to confluence and use the ri:attachment tag</span>
        <span class="keyword">if</span>(!src.startsWith(<span class="string"><span class="delimiter">&quot;</span><span class="content">http</span><span class="delimiter">&quot;</span></span>)) {
          <span class="keyword">def</span> newUrl = baseUrl.toString().replaceAll(<span class="string"><span class="delimiter">'</span><span class="char">\\</span><span class="char">\\</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">/</span><span class="delimiter">'</span></span>).replaceAll(<span class="string"><span class="delimiter">'</span><span class="content">/[^/]*$</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">/</span><span class="delimiter">'</span></span>)+src
          <span class="keyword">def</span> fileName = java.net.URLDecoder.decode((src.tokenize(<span class="string"><span class="delimiter">'</span><span class="content">/</span><span class="delimiter">'</span></span>)[-<span class="integer">1</span>]),<span class="string"><span class="delimiter">&quot;</span><span class="content">UTF-8</span><span class="delimiter">&quot;</span></span>)
          newUrl = java.net.URLDecoder.decode(newUrl,<span class="string"><span class="delimiter">&quot;</span><span class="content">UTF-8</span><span class="delimiter">&quot;</span></span>)

          trythis {
              deferredUpload &lt;&lt;  [<span class="integer">0</span>,newUrl,fileName,<span class="string"><span class="delimiter">&quot;</span><span class="content">automatically uploaded</span><span class="delimiter">&quot;</span></span>]
          }
          img.after(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;ac:image ac:align=</span><span class="char">\&quot;</span><span class="inline"><span class="inline-delimiter">${</span>imgAlign<span class="inline-delimiter">}</span></span><span class="char">\&quot;</span><span class="content"> ac:width=</span><span class="char">\&quot;</span><span class="inline"><span class="inline-delimiter">${</span>imgWidth<span class="inline-delimiter">}</span></span><span class="char">\&quot;</span><span class="content">&gt;&lt;ri:attachment ri:filename=</span><span class="char">\&quot;</span><span class="inline"><span class="inline-delimiter">${</span>fileName<span class="inline-delimiter">}</span></span><span class="char">\&quot;</span><span class="content">/&gt;&lt;/ac:image&gt;</span><span class="delimiter">&quot;</span></span>)
        }
        <span class="comment">// it is an online image, so we have to use the ri:url tag</span>
        <span class="keyword">else</span> {
          img.after(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;ac:image ac:align=</span><span class="char">\&quot;</span><span class="content">imgAlign</span><span class="char">\&quot;</span><span class="content"> ac:width=</span><span class="char">\&quot;</span><span class="inline"><span class="inline-delimiter">${</span>imgWidth<span class="inline-delimiter">}</span></span><span class="char">\&quot;</span><span class="content">&gt;&lt;ri:url ri:value=</span><span class="char">\&quot;</span><span class="inline"><span class="inline-delimiter">${</span>src<span class="inline-delimiter">}</span></span><span class="char">\&quot;</span><span class="content">/&gt;&lt;/ac:image&gt;</span><span class="delimiter">&quot;</span></span>)
        }
        img.remove()
    }


    <span class="keyword">if</span>(config.confluence.enableAttachments){
        attachmentPrefix = config.confluence.attachmentPrefix ? config.confluence.attachmentPrefix : <span class="string"><span class="delimiter">'</span><span class="content">attachment</span><span class="delimiter">'</span></span>
        body.select(<span class="string"><span class="delimiter">'</span><span class="content">a</span><span class="delimiter">'</span></span>).each { link -&gt;

            <span class="keyword">def</span> src = link.attr(<span class="string"><span class="delimiter">'</span><span class="content">href</span><span class="delimiter">'</span></span>)
            println <span class="string"><span class="delimiter">&quot;</span><span class="content">    attachment src: </span><span class="delimiter">&quot;</span></span>+src

            <span class="comment">//upload it to confluence and use the ri:attachment tag</span>
            <span class="keyword">if</span>(src.startsWith(attachmentPrefix)) {
                <span class="keyword">def</span> newUrl = baseUrl.toString().replaceAll(<span class="string"><span class="delimiter">'</span><span class="char">\\</span><span class="char">\\</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">/</span><span class="delimiter">'</span></span>).replaceAll(<span class="string"><span class="delimiter">'</span><span class="content">/[^/]*$</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">/</span><span class="delimiter">'</span></span>)+src
                <span class="keyword">def</span> fileName = java.net.URLDecoder.decode((src.tokenize(<span class="string"><span class="delimiter">'</span><span class="content">/</span><span class="delimiter">'</span></span>)[-<span class="integer">1</span>]),<span class="string"><span class="delimiter">&quot;</span><span class="content">UTF-8</span><span class="delimiter">&quot;</span></span>)
                newUrl = java.net.URLDecoder.decode(newUrl,<span class="string"><span class="delimiter">&quot;</span><span class="content">UTF-8</span><span class="delimiter">&quot;</span></span>)

                trythis {
                    deferredUpload &lt;&lt;  [<span class="integer">0</span>,newUrl,fileName,<span class="string"><span class="delimiter">&quot;</span><span class="content">automatically uploaded non-image attachment by docToolchain</span><span class="delimiter">&quot;</span></span>]
                }
                <span class="keyword">def</span> uriArray=fileName.split(<span class="string"><span class="delimiter">&quot;</span><span class="content">/</span><span class="delimiter">&quot;</span></span>)
                <span class="keyword">def</span> pureFilename = uriArray[uriArray.length-<span class="integer">1</span>]
                <span class="keyword">def</span> innerhtml = link.html()
                link.after(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;ac:structured-macro ac:name=</span><span class="char">\&quot;</span><span class="content">view-file</span><span class="char">\&quot;</span><span class="content"> ac:schema-version=</span><span class="char">\&quot;</span><span class="content">1</span><span class="char">\&quot;</span><span class="content">&gt;&lt;ac:parameter ac:name=</span><span class="char">\&quot;</span><span class="content">name</span><span class="char">\&quot;</span><span class="content">&gt;&lt;ri:attachment ri:filename=</span><span class="char">\&quot;</span><span class="inline"><span class="inline-delimiter">${</span>pureFilename<span class="inline-delimiter">}</span></span><span class="char">\&quot;</span><span class="content">/&gt;&lt;/ac:parameter&gt;&lt;/ac:structured-macro&gt;</span><span class="delimiter">&quot;</span></span>)
                link.after(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;ac:link&gt;&lt;ri:attachment ri:filename=</span><span class="char">\&quot;</span><span class="inline"><span class="inline-delimiter">${</span>pureFilename<span class="inline-delimiter">}</span></span><span class="char">\&quot;</span><span class="content">/&gt;&lt;ac:plain-text-link-body&gt; &lt;![CDATA[</span><span class="char">\&quot;</span><span class="inline"><span class="inline-delimiter">${</span>innerhtml<span class="inline-delimiter">}</span></span><span class="char">\&quot;</span><span class="content">]]&gt;&lt;/ac:plain-text-link-body&gt;&lt;/ac:link&gt;</span><span class="delimiter">&quot;</span></span>)
                link.remove()

            }
        }
    }

    rewriteMarks body
    rewriteDescriptionLists body
    rewriteInternalLinks body, anchors, pageAnchors
    <span class="comment">//sanitize code inside code tags</span>
    rewriteCodeblocks body
    <span class="keyword">def</span> pageString = unescapeCDATASections body.html().trim()

    <span class="comment">//change some html elements through simple substitutions</span>
    pageString = pageString
            .replaceAll(<span class="string"><span class="delimiter">'</span><span class="content">&lt;br&gt;</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">&lt;br /&gt;</span><span class="delimiter">'</span></span>)
            .replaceAll(<span class="string"><span class="delimiter">'</span><span class="content">&lt;/br&gt;</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">&lt;br /&gt;</span><span class="delimiter">'</span></span>)
            .replaceAll(<span class="string"><span class="delimiter">'</span><span class="content">&lt;a([^&gt;]*)&gt;&lt;/a&gt;</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>)
            .replaceAll(CDATA_PLACEHOLDER_START,<span class="string"><span class="delimiter">'</span><span class="content">&lt;![CDATA[</span><span class="delimiter">'</span></span>)
            .replaceAll(CDATA_PLACEHOLDER_END,<span class="string"><span class="delimiter">'</span><span class="content">]]&gt;</span><span class="delimiter">'</span></span>)

    <span class="keyword">return</span> pageString
}

<span class="comment">// the create-or-update functionality for confluence pages</span>
<span class="comment">// #342-dierk42: added parameter 'keywords'</span>
<span class="keyword">def</span> pushToConfluence = { pageTitle, pageBody, <span class="predefined-type">String</span> parentId, anchors, pageAnchors, keywords -&gt;
    <span class="keyword">def</span> api = <span class="keyword">new</span> RESTClient(config.confluence.api)
    <span class="keyword">def</span> headers = [
            <span class="string"><span class="delimiter">'</span><span class="content">Authorization</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">'</span><span class="content">Basic </span><span class="delimiter">'</span></span> + config.confluence.credentials,
            <span class="string"><span class="delimiter">'</span><span class="content">Content-Type</span><span class="delimiter">'</span></span>:<span class="string"><span class="delimiter">'</span><span class="content">application/json; charset=utf-8</span><span class="delimiter">'</span></span>
    ]
    <span class="comment">//Add api key and value to REST API request header if configured - required for authentification.</span>
    <span class="keyword">if</span> (config.confluence.apikey)
    {
       headers.keyid = config.confluence.apikey
    }
    <span class="predefined-type">String</span> realTitleLC = realTitle(pageTitle).toLowerCase()

    <span class="comment">//this fixes the encoding</span>
    api.encoderRegistry = <span class="keyword">new</span> EncoderRegistry( <span class="key">charset</span>: <span class="string"><span class="delimiter">'</span><span class="content">utf-8</span><span class="delimiter">'</span></span> )
    <span class="keyword">if</span> (config.confluence.proxy) {
        api.setProxy(config.confluence.proxy.host, config.confluence.proxy.port, config.confluence.proxy.schema ?: <span class="string"><span class="delimiter">'</span><span class="content">http</span><span class="delimiter">'</span></span>)
    }
    <span class="comment">//try to get an existing page</span>
    localPage = parseBody(pageBody, anchors, pageAnchors)
    <span class="keyword">def</span> localHash = MD5(localPage)
    <span class="keyword">if</span>(config.confluence.disableToC){
        <span class="keyword">def</span> prefix = (config.confluence.extraPageContent?:<span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>)
        localPage  = prefix+localPage
        localHash = MD5(localPage)
        localPage += <span class="string"><span class="delimiter">'</span><span class="content">&lt;p style=&quot;display:none&quot;&gt;hash: #</span><span class="delimiter">'</span></span>+localHash+<span class="string"><span class="delimiter">'</span><span class="content">#&lt;/p&gt;</span><span class="delimiter">'</span></span>
    }<span class="keyword">else</span>{
        <span class="keyword">def</span> default_toc = <span class="string"><span class="delimiter">'</span><span class="content">&lt;p&gt;&lt;ac:structured-macro ac:name=&quot;toc&quot;/&gt;&lt;/p&gt;</span><span class="delimiter">'</span></span>
        <span class="keyword">def</span> prefix = (config.confluence.tableOfContents?:default_toc)+(config.confluence.extraPageContent?:<span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>)
        localPage  = prefix+localPage
        <span class="keyword">def</span> default_children = <span class="string"><span class="delimiter">'</span><span class="content">&lt;p&gt;&lt;ac:structured-macro ac:name=&quot;children&quot;&gt;&lt;ac:parameter ac:name=&quot;sort&quot;&gt;creation&lt;/ac:parameter&gt;&lt;/ac:structured-macro&gt;&lt;/p&gt;</span><span class="delimiter">'</span></span>
        localPage += (config.confluence.tableOfChildren?:default_children)
        localHash = MD5(localPage)
        localPage += <span class="string"><span class="delimiter">'</span><span class="content">&lt;p style=&quot;display:none&quot;&gt;hash: #</span><span class="delimiter">'</span></span>+localHash+<span class="string"><span class="delimiter">'</span><span class="content">#&lt;/p&gt;</span><span class="delimiter">'</span></span>
    }


    <span class="keyword">def</span> request = [
            type : <span class="string"><span class="delimiter">'</span><span class="content">page</span><span class="delimiter">'</span></span>,
            <span class="key">title</span>: realTitle(pageTitle),
            <span class="key">space</span>: [
                    <span class="key">key</span>: confluenceSpaceKey
            ],
            body : [
                    <span class="key">storage</span>: [
                            value         : localPage,
                            <span class="key">representation</span>: <span class="string"><span class="delimiter">'</span><span class="content">storage</span><span class="delimiter">'</span></span>
                    ]
            ]
    ]
    <span class="keyword">if</span> (parentId) {
        request.ancestors = [
                [ <span class="key">type</span>: <span class="string"><span class="delimiter">'</span><span class="content">page</span><span class="delimiter">'</span></span>, <span class="key">id</span>: parentId]
        ]
    }

    <span class="keyword">def</span> pages = retrieveAllPages(api, headers, config.confluence.spaceKey)

    <span class="comment">// println &quot;Suche nach vorhandener Seite: &quot; + pageTitle</span>
    <span class="predefined-type">Map</span> existingPage = pages[realTitleLC]
    <span class="keyword">def</span> page

    <span class="keyword">if</span> (existingPage) {
        <span class="keyword">if</span> (hasRequestedParent(existingPage, parentId)) {
            page = retrieveFullPage(api, headers, existingPage.id)
        } <span class="keyword">else</span> {
            page = <span class="predefined-constant">null</span>
        }
    } <span class="keyword">else</span> {
        page = <span class="predefined-constant">null</span>
    }
    <span class="comment">// println &quot;Gefunden: &quot; + page.id + &quot; Titel: &quot; + page.title</span>

    <span class="keyword">if</span> (page) {
        println <span class="string"><span class="delimiter">&quot;</span><span class="content">found existing page: </span><span class="delimiter">&quot;</span></span> + page.id +<span class="string"><span class="delimiter">&quot;</span><span class="content"> version </span><span class="delimiter">&quot;</span></span>+page.version.number

        <span class="comment">//extract hash from remote page to see if it is different from local one</span>

        <span class="keyword">def</span> remotePage = page.body.storage.value.toString().trim()

        <span class="keyword">def</span> remoteHash = remotePage =~ <span class="regexp"><span class="delimiter">/</span><span class="content">(?ms)hash: #([^#]+)#</span><span class="delimiter">/</span></span>
        remoteHash = remoteHash.size()==<span class="integer">0</span>?<span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span>:remoteHash[<span class="integer">0</span>][<span class="integer">1</span>]

        <span class="comment">// println &quot;remoteHash: &quot; + remoteHash</span>
        <span class="comment">// println &quot;localHash:  &quot; + localHash</span>

        <span class="keyword">if</span> (remoteHash == localHash) {
            println <span class="string"><span class="delimiter">&quot;</span><span class="content">page hasn't changed!</span><span class="delimiter">&quot;</span></span>
            deferredUpload.each {
                uploadAttachment(page?.id, <span class="local-variable">it</span>[<span class="integer">1</span>], <span class="local-variable">it</span>[<span class="integer">2</span>], <span class="local-variable">it</span>[<span class="integer">3</span>])
            }
            deferredUpload = <span class="type">[]</span>
            <span class="comment">// #324-dierk42: Add keywords as labels to page.</span>
            <span class="keyword">if</span> (keywords) {
                addLabels(page.id, keywords)
            }
            <span class="keyword">return</span> page.id
        } <span class="keyword">else</span> {
            trythis {
                <span class="comment">// update page</span>
                <span class="comment">// https://developer.atlassian.com/display/CONFDEV/Confluence+REST+API+Examples#ConfluenceRESTAPIExamples-Updatingapage</span>
                request.id      = page.id
                request.version = [<span class="key">number</span>: (page.version.number <span class="keyword">as</span> <span class="predefined-type">Integer</span>) + <span class="integer">1</span>]
                <span class="keyword">def</span> res = api.put(<span class="key">contentType</span>: ContentType.JSON,
                                  requestContentType : ContentType.JSON,
                                  <span class="key">path</span>: <span class="string"><span class="delimiter">'</span><span class="content">content/</span><span class="delimiter">'</span></span> + page.id, <span class="key">body</span>: request, <span class="key">headers</span>: headers)
            }
            println <span class="string"><span class="delimiter">&quot;</span><span class="content">&gt; updated page </span><span class="delimiter">&quot;</span></span>+page.id
            deferredUpload.each {
                uploadAttachment(page.id, <span class="local-variable">it</span>[<span class="integer">1</span>], <span class="local-variable">it</span>[<span class="integer">2</span>], <span class="local-variable">it</span>[<span class="integer">3</span>])
            }
            deferredUpload = <span class="type">[]</span>
            <span class="comment">// #324-dierk42: Add keywords as labels to page.</span>
            <span class="keyword">if</span> (keywords) {
                addLabels(page.id, keywords)
            }
            <span class="keyword">return</span> page.id
        }
    } <span class="keyword">else</span> {
        <span class="comment">//#352-LuisMuniz if the existing page's parent does not match the requested parentId, fail</span>
        <span class="keyword">if</span> (existingPage &amp;&amp; !hasRequestedParent(existingPage, parentId)) {
            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="exception">IllegalArgumentException</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">Cannot create page, page with the same </span><span class="delimiter">&quot;</span></span>
                    + <span class="string"><span class="delimiter">&quot;</span><span class="content">title=</span><span class="inline"><span class="inline-delimiter">${</span>existingPage.title<span class="inline-delimiter">}</span></span><span class="content"> </span><span class="delimiter">&quot;</span></span>
                    + <span class="string"><span class="delimiter">&quot;</span><span class="content">with id=</span><span class="inline"><span class="inline-delimiter">${</span>existingPage.id<span class="inline-delimiter">}</span></span><span class="content"> already exists in the space. </span><span class="delimiter">&quot;</span></span>
                    + <span class="string"><span class="delimiter">&quot;</span><span class="content">A Confluence page title must be unique within a space, consider specifying a 'confluencePagePrefix' in ConfluenceConfig.groovy</span><span class="delimiter">&quot;</span></span>)
        }

        <span class="comment">//create a page</span>
        trythis {
            page = api.post(<span class="key">contentType</span>: ContentType.JSON,
                            <span class="key">requestContentType</span>: ContentType.JSON,
                            <span class="key">path</span>: <span class="string"><span class="delimiter">'</span><span class="content">content</span><span class="delimiter">'</span></span>, <span class="key">body</span>: request, <span class="key">headers</span>: headers)
        }
        println <span class="string"><span class="delimiter">&quot;</span><span class="content">&gt; created page </span><span class="delimiter">&quot;</span></span>+page?.data?.id
        deferredUpload.each {
            uploadAttachment(page?.data?.id, <span class="local-variable">it</span>[<span class="integer">1</span>], <span class="local-variable">it</span>[<span class="integer">2</span>], <span class="local-variable">it</span>[<span class="integer">3</span>])
        }
        deferredUpload = <span class="type">[]</span>
        <span class="comment">// #324-dierk42: Add keywords as labels to page.</span>
        <span class="keyword">if</span> (keywords) {
            addLabels(page?.data?.id, keywords)
        }
        <span class="keyword">return</span> page?.data?.id
    }
}

<span class="keyword">def</span> parseAnchors = { page -&gt;
    <span class="keyword">def</span> anchors = [:]
    page.body.select(<span class="string"><span class="delimiter">'</span><span class="content">[id]</span><span class="delimiter">'</span></span>).each { anchor -&gt;
        <span class="keyword">def</span> name = anchor.attr(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>)
        anchors[name] = page.title
        anchor.before(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;ac:structured-macro ac:name=</span><span class="char">\&quot;</span><span class="content">anchor</span><span class="char">\&quot;</span><span class="content">&gt;&lt;ac:parameter ac:name=</span><span class="char">\&quot;</span><span class="char">\&quot;</span><span class="content">&gt;</span><span class="inline"><span class="inline-delimiter">${</span>name<span class="inline-delimiter">}</span></span><span class="content">&lt;/ac:parameter&gt;&lt;/ac:structured-macro&gt;</span><span class="delimiter">&quot;</span></span>)
    }
    anchors
}

<span class="keyword">def</span> pushPages
pushPages = { pages, anchors, pageAnchors, labels -&gt;
    pages.each { page -&gt;
        println page.title
        <span class="keyword">def</span> id = pushToConfluence page.title, page.body, page.parent, anchors, pageAnchors, labels
        page.children*.parent = id
        <span class="comment">// println &quot;Push children von id &quot; + id</span>
        pushPages page.children, anchors, pageAnchors, labels
        <span class="comment">// println &quot;Ende Push children von id &quot; + id</span>
    }
}

<span class="keyword">def</span> recordPageAnchor = { head -&gt;
    <span class="keyword">def</span> a = [:]
    <span class="keyword">if</span> (head.attr(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>)) {
        a[head.attr(<span class="string"><span class="delimiter">'</span><span class="content">id</span><span class="delimiter">'</span></span>)] = head.text()
    }
    a
}

<span class="keyword">def</span> promoteHeaders = { tree, start, offset -&gt;
    (start..<span class="integer">7</span>).each { i -&gt;
        tree.select(<span class="string"><span class="delimiter">&quot;</span><span class="content">h</span><span class="inline"><span class="inline-delimiter">${</span>i<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>).tagName(<span class="string"><span class="delimiter">&quot;</span><span class="content">h</span><span class="inline"><span class="inline-delimiter">${</span>i-offset<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>).before(<span class="string"><span class="delimiter">'</span><span class="content">&lt;br /&gt;</span><span class="delimiter">'</span></span>)
    }
}

config.confluence.input.each { input -&gt;

    input.file = <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">${</span>docDir<span class="inline-delimiter">}</span></span><span class="content">/</span><span class="inline"><span class="inline-delimiter">${</span>input.file<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>

    println <span class="string"><span class="delimiter">&quot;</span><span class="content">publish </span><span class="inline"><span class="inline-delimiter">${</span>input.file<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>

    <span class="keyword">if</span> (input.file ==~ <span class="regexp"><span class="delimiter">/</span><span class="content">.*[.](ad|adoc|asciidoc)</span><span class="content">$</span><span class="delimiter">/</span></span>) {
        println <span class="string"><span class="delimiter">&quot;</span><span class="content">convert </span><span class="inline"><span class="inline-delimiter">${</span>input.file<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>
        <span class="string"><span class="delimiter">&quot;</span><span class="content">groovy asciidoc2html.groovy </span><span class="inline"><span class="inline-delimiter">${</span>input.file<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>.execute()
        input.file = input.file.replaceAll(<span class="regexp"><span class="delimiter">/</span><span class="content">[.](ad|adoc|asciidoc)</span><span class="content">$</span><span class="delimiter">/</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">.html</span><span class="delimiter">'</span></span>)
        println <span class="string"><span class="delimiter">&quot;</span><span class="content">to </span><span class="inline"><span class="inline-delimiter">${</span>input.file<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>
    }
<span class="comment">//  assignend, but never used in pushToConfluence(...) (fixed here)</span>
    confluenceSpaceKey = input.spaceKey ?: config.confluence.spaceKey
    confluenceCreateSubpages = (input.createSubpages != <span class="predefined-constant">null</span>) ? input.createSubpages : config.confluence.createSubpages
<span class="comment">//  hard to read in case of using :sectnums: -&gt; so we add a suffix</span>
    confluencePagePrefix = input.pagePrefix ?: config.confluence.pagePrefix
<span class="comment">//  added</span>
    confluencePageSuffix = input.pageSuffix ?: config.confluence.pageSuffix
    confluencePreambleTitle = input.preambleTitle ?: config.confluence.preambleTitle

    <span class="keyword">def</span> html = input.file ? <span class="keyword">new</span> <span class="predefined-type">File</span>(input.file).getText(<span class="string"><span class="delimiter">'</span><span class="content">utf-8</span><span class="delimiter">'</span></span>) : <span class="keyword">new</span> <span class="predefined-type">URL</span>(input.url).getText()
    baseUrl = input.file ? <span class="keyword">new</span> <span class="predefined-type">File</span>(input.file) : <span class="keyword">new</span> <span class="predefined-type">URL</span>(input.url)
    <span class="predefined-type">Document</span> dom = Jsoup.parse(html, <span class="string"><span class="delimiter">'</span><span class="content">utf-8</span><span class="delimiter">'</span></span>, <span class="predefined-type">Parser</span>.xmlParser())
    dom.outputSettings().prettyPrint(<span class="predefined-constant">false</span>);<span class="comment">//makes html() preserve linebreaks and spacing</span>
    dom.outputSettings().escapeMode(org.jsoup.nodes.Entities.EscapeMode.xhtml); <span class="comment">//This will ensure xhtml validity regarding entities</span>
    dom.outputSettings().charset(<span class="string"><span class="delimiter">&quot;</span><span class="content">UTF-8</span><span class="delimiter">&quot;</span></span>); <span class="comment">//does no harm :-)</span>

    <span class="comment">//if input does not contain an ancestorId, check if there is a global one</span>
    <span class="keyword">def</span> parentId = input.ancestorId ?: config.confluence.ancestorId
    <span class="comment">// if parentId is still not set, create a new parent page (parentId = null)</span>
    parentId = parentId ?: <span class="predefined-constant">null</span>
    <span class="keyword">def</span> anchors = [:]
    <span class="keyword">def</span> pageAnchors = [:]
    <span class="keyword">def</span> sections = pages = <span class="type">[]</span>
    <span class="comment">// #342-dierk42: get the keywords from the meta tags</span>
    <span class="keyword">def</span> keywords = <span class="type">[]</span>
    dom.select(<span class="string"><span class="delimiter">'</span><span class="content">meta[name=keywords]</span><span class="delimiter">'</span></span>).each { kw -&gt;
        kws = kw.attr(<span class="string"><span class="delimiter">'</span><span class="content">content</span><span class="delimiter">'</span></span>).split(<span class="string"><span class="delimiter">'</span><span class="content">,</span><span class="delimiter">'</span></span>)
        kws.each { skw -&gt;
            keywords &lt;&lt; skw.trim()
        }
        println <span class="string"><span class="delimiter">&quot;</span><span class="content">Keywords:</span><span class="delimiter">&quot;</span></span> + keywords
    }
    <span class="comment">// let's try to select the &quot;first page&quot; and push it to confluence</span>
    dom.select(<span class="string"><span class="delimiter">'</span><span class="content">div#preamble div.sectionbody</span><span class="delimiter">'</span></span>).each { pageBody -&gt;
        pageBody.select(<span class="string"><span class="delimiter">'</span><span class="content">div.sect2</span><span class="delimiter">'</span></span>).unwrap()
        <span class="keyword">def</span> preamble = [
            <span class="key">title</span>: confluencePreambleTitle ?: <span class="string"><span class="delimiter">&quot;</span><span class="content">arc42</span><span class="delimiter">&quot;</span></span>,
            <span class="key">body</span>: pageBody,
            <span class="key">children</span>: <span class="type">[]</span>,
            <span class="key">parent</span>: parentId
        ]
        pages &lt;&lt; preamble
        sections = preamble.children
        parentId = <span class="predefined-constant">null</span>
        anchors.putAll(parseAnchors(preamble))
    }
    <span class="comment">// &lt;div class=&quot;sect1&quot;&gt; are the main headings</span>
    <span class="comment">// let's extract these</span>
    dom.select(<span class="string"><span class="delimiter">'</span><span class="content">div.sect1</span><span class="delimiter">'</span></span>).each { sect1 -&gt;
        Elements pageBody = sect1.select(<span class="string"><span class="delimiter">'</span><span class="content">div.sectionbody</span><span class="delimiter">'</span></span>)
        <span class="keyword">def</span> currentPage = [
            <span class="key">title</span>: sect1.select(<span class="string"><span class="delimiter">'</span><span class="content">h2</span><span class="delimiter">'</span></span>).text(),
            <span class="key">body</span>: pageBody,
            <span class="key">children</span>: <span class="type">[]</span>,
            <span class="key">parent</span>: parentId
        ]
        pageAnchors.putAll(recordPageAnchor(sect1.select(<span class="string"><span class="delimiter">'</span><span class="content">h2</span><span class="delimiter">'</span></span>)))

        <span class="keyword">if</span> (confluenceCreateSubpages) {
            pageBody.select(<span class="string"><span class="delimiter">'</span><span class="content">div.sect2</span><span class="delimiter">'</span></span>).each { sect2 -&gt;
                <span class="keyword">def</span> title = sect2.select(<span class="string"><span class="delimiter">'</span><span class="content">h3</span><span class="delimiter">'</span></span>).text()
                pageAnchors.putAll(recordPageAnchor(sect2.select(<span class="string"><span class="delimiter">'</span><span class="content">h3</span><span class="delimiter">'</span></span>)))
                sect2.select(<span class="string"><span class="delimiter">'</span><span class="content">h3</span><span class="delimiter">'</span></span>).remove()
                <span class="keyword">def</span> body = Jsoup.parse(sect2.toString(),<span class="string"><span class="delimiter">'</span><span class="content">utf-8</span><span class="delimiter">'</span></span>, <span class="predefined-type">Parser</span>.xmlParser())
                body.outputSettings(<span class="keyword">new</span> <span class="predefined-type">Document</span>.OutputSettings().prettyPrint(<span class="predefined-constant">false</span>))
                <span class="keyword">def</span> subPage = [
                    <span class="key">title</span>: title,
                    <span class="key">body</span>: body
                ]
                currentPage.children &lt;&lt; subPage
                promoteHeaders sect2, <span class="integer">4</span>, <span class="integer">3</span>
                anchors.putAll(parseAnchors(subPage))
            }
            pageBody.select(<span class="string"><span class="delimiter">'</span><span class="content">div.sect2</span><span class="delimiter">'</span></span>).remove()
        } <span class="keyword">else</span> {
            pageBody.select(<span class="string"><span class="delimiter">'</span><span class="content">div.sect2</span><span class="delimiter">'</span></span>).unwrap()
            promoteHeaders sect1, <span class="integer">3</span>, <span class="integer">2</span>
        }
        sections &lt;&lt; currentPage
        anchors.putAll(parseAnchors(currentPage))
    }

    pushPages pages, anchors, pageAnchors, keywords
    <span class="keyword">if</span> (parentId) {
        println <span class="string"><span class="delimiter">&quot;</span><span class="content">published to </span><span class="inline"><span class="inline-delimiter">${</span>config.confluence.api - <span class="string"><span class="delimiter">&quot;</span><span class="content">rest/api/</span><span class="delimiter">&quot;</span></span><span class="inline-delimiter">}</span></span><span class="content">spaces/</span><span class="inline"><span class="inline-delimiter">${</span>confluenceSpaceKey<span class="inline-delimiter">}</span></span><span class="content">/pages/</span><span class="inline"><span class="inline-delimiter">${</span>parentId<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>
    } <span class="keyword">else</span> {
        println <span class="string"><span class="delimiter">&quot;</span><span class="content">published to </span><span class="inline"><span class="inline-delimiter">${</span>config.confluence.api - <span class="string"><span class="delimiter">&quot;</span><span class="content">rest/api/</span><span class="delimiter">&quot;</span></span><span class="inline-delimiter">}</span></span><span class="content">spaces/</span><span class="inline"><span class="inline-delimiter">${</span>confluenceSpaceKey<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>
    }
}
<span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span></code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect2">
<h3 id="_converttodocx">3.10. convertToDocx</h3>
<div class="ulist">
<ul>
<li>
<p>Needs <a href="https://pandoc.org">pandoc</a> installed</p>
</li>
<li>
<p>Please make sure that 'docbook' and 'docx' are added to the inputFiles formats in Config.groovy</p>
</li>
<li>
<p>Optional: you can specify a reference doc file with custom stylesheets (see task <a href="#_createreferencedoc">createReferenceDoc</a>)</p>
</li>
</ul>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_convertToDocx.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_convertToDocx.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5107bc93f3e4d103ca06f4e68a64b38?d=identicon" alt="Heiko Stehli " width="32" title="Heiko Stehli "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/69124393772349d32fb9fe975545757d?d=identicon" alt="Lars Francke " width="32" title="Lars Francke "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/61c257b76a94d988f747a87850b7e114?d=identicon" alt="ghp-dev " width="32" title="ghp-dev "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>1 minute to read</pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="images/ea/Manual/convertToDocx.png" alt="convertToDocx">
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Blog-Post: <a href="https://rdmueller.github.io/create-docx/">Render AsciiDoc to docx (MS Word)</a>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_source_6">3.10.1. Source</h4>
<div class="listingblock">
<div class="title">pandoc.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">task convertToDocx (
        <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">docToolchain</span><span class="delimiter">'</span></span>,
        <span class="key">description</span>: <span class="string"><span class="delimiter">'</span><span class="content">converts file to .docx via pandoc. Needs pandoc installed.</span><span class="delimiter">'</span></span>,
        <span class="key">type</span>: Exec
) {
    <span class="comment">// All files with option `docx` in config.groovy is converted to docbook and then to docx.</span>
    <span class="keyword">def</span> sourceFilesDocx = sourceFiles.findAll { <span class="string"><span class="delimiter">'</span><span class="content">docx</span><span class="delimiter">'</span></span> <span class="keyword">in</span> <span class="local-variable">it</span>.formats }
    sourceFilesDocx.each {
        <span class="keyword">def</span> sourceFile = <span class="local-variable">it</span>.file.replace(<span class="string"><span class="delimiter">'</span><span class="content">.adoc</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">.xml</span><span class="delimiter">'</span></span>)
        <span class="keyword">def</span> targetFile = sourceFile.replace(<span class="string"><span class="delimiter">'</span><span class="content">.xml</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">.docx</span><span class="delimiter">'</span></span>)

        workingDir <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">$</span>targetDir</span><span class="content">/docbook</span><span class="delimiter">&quot;</span></span>
        executable = <span class="string"><span class="delimiter">&quot;</span><span class="content">pandoc</span><span class="delimiter">&quot;</span></span>

        <span class="keyword">if</span>(referenceDocFile?.trim()) {
            args = [<span class="string"><span class="delimiter">&quot;</span><span class="content">-r</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">docbook</span><span class="delimiter">&quot;</span></span>,
                    <span class="string"><span class="delimiter">&quot;</span><span class="content">-t</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">docx</span><span class="delimiter">&quot;</span></span>,
                    <span class="string"><span class="delimiter">&quot;</span><span class="content">-o</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">../docx/</span><span class="inline"><span class="inline-delimiter">$</span>targetFile</span><span class="delimiter">&quot;</span></span>,
                    <span class="string"><span class="delimiter">&quot;</span><span class="content">--reference-doc=</span><span class="inline"><span class="inline-delimiter">${</span>docDir<span class="inline-delimiter">}</span></span><span class="content">/</span><span class="inline"><span class="inline-delimiter">${</span>referenceDocFile<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>,
                    sourceFile]
        } <span class="keyword">else</span> {
            args = [<span class="string"><span class="delimiter">&quot;</span><span class="content">-r</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">docbook</span><span class="delimiter">&quot;</span></span>,
                    <span class="string"><span class="delimiter">&quot;</span><span class="content">-t</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">docx</span><span class="delimiter">&quot;</span></span>,
                    <span class="string"><span class="delimiter">&quot;</span><span class="content">-o</span><span class="delimiter">&quot;</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">../docx/</span><span class="inline"><span class="inline-delimiter">$</span>targetFile</span><span class="delimiter">&quot;</span></span>,
                    sourceFile]
        }
    }
    doFirst {
        <span class="keyword">new</span> <span class="predefined-type">File</span>(<span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">$</span>targetDir</span><span class="content">/docx/</span><span class="delimiter">&quot;</span></span>).mkdirs()
    }
}</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect2">
<h3 id="_createreferencedoc">3.11. createReferenceDoc</h3>
<div class="ulist">
<ul>
<li>
<p>Needs <a href="https://pandoc.org">pandoc</a> installed</p>
</li>
</ul>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_createReferenceDoc.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_createReferenceDoc.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/5107bc93f3e4d103ca06f4e68a64b38?d=identicon" alt="Heiko Stehli " width="32" title="Heiko Stehli "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>1 minute to read</pre>
</div>
</div>
<div class="paragraph">
<p>Creates a reference docx file used by <a href="https://pandoc.org">pandoc</a> during conversion from docbook to docx.
You can edit this file to use your prefered styles in task <a href="#_converttodocx">convertToDocx</a>.</p>
</div>
<div class="paragraph">
<p>Please make sure that 'referenceDocFile' property is set to your custom reference file in Config.groovy</p>
</div>
<div class="listingblock">
<div class="title">Excerpt of Config.groovy with property 'referenceDocFile'</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">inputPath = <span class="string"><span class="delimiter">'</span><span class="content">.</span><span class="delimiter">'</span></span>

<span class="comment">// use a style reference file in the input path for conversion from docbook to docx</span>
referenceDocFile = <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">${</span>inputPath<span class="inline-delimiter">}</span></span><span class="content">/my-ref-file.docx</span><span class="delimiter">&quot;</span></span></code></pre>
</div>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The contents of the reference docx are ignored, but its stylesheets and document properties (including margins, page size, header, and footer) are used in the new docx.</p>
</div>
<div class="paragraph">
<p>See Pandoc User&#8217;s Guide: <a href="https://pandoc.org/MANUAL.html#options-affecting-specific-writers">Options affecting specific writers (--reference-doc)</a></p>
</div>
<div class="paragraph">
<p>If you have problems with changing the default table style: see <a href="https://github.com/jgm/pandoc/issues/3275" class="bare">https://github.com/jgm/pandoc/issues/3275</a>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_source_7">3.11.1. Source</h4>
<div class="listingblock">
<div class="title">pandoc.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">task createReferenceDoc (
        <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">docToolchain helper</span><span class="delimiter">'</span></span>,
        <span class="key">description</span>: <span class="string"><span class="delimiter">'</span><span class="content">creates a docx file to be used as a format style reference in task convertToDocx. Needs pandoc installed.</span><span class="delimiter">'</span></span>,
        <span class="key">type</span>: Exec
) {
    workingDir <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">$</span>docDir</span><span class="delimiter">&quot;</span></span>
    executable = <span class="string"><span class="delimiter">&quot;</span><span class="content">pandoc</span><span class="delimiter">&quot;</span></span>
    args = [<span class="string"><span class="delimiter">&quot;</span><span class="content">-o</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">${</span>docDir<span class="inline-delimiter">}</span></span><span class="content">/</span><span class="inline"><span class="inline-delimiter">${</span>referenceDocFile<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>,
            <span class="string"><span class="delimiter">&quot;</span><span class="content">--print-default-data-file</span><span class="delimiter">&quot;</span></span>,
            <span class="string"><span class="delimiter">&quot;</span><span class="content">reference.docx</span><span class="delimiter">&quot;</span></span>]

    doFirst {
        <span class="keyword">if</span>(!(referenceDocFile?.trim())) {
            <span class="keyword">throw</span> <span class="keyword">new</span> GradleException(<span class="string"><span class="delimiter">&quot;</span><span class="content">Option `referenceDocFile` is not defined in config.groovy or has an empty value.</span><span class="delimiter">&quot;</span></span>)
        }
    }
}</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect2">
<h3 id="_converttoepub">3.12. convertToEpub</h3>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_convertToEpub.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_convertToEpub.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/34ef1437fd31ff96c31dff58125e93fe?d=identicon" alt="Dr. Stefan Pfeiffer " width="32" title="Dr. Stefan Pfeiffer "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/69124393772349d32fb9fe975545757d?d=identicon" alt="Lars Francke " width="32" title="Lars Francke "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>1 minute to read</pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="images/ea/Manual/convertToEpub.png" alt="convertToEpub">
</div>
</div>
<div class="paragraph">
<p>Dependency: <a href="#_generatedocbook">generateDocbook</a></p>
</div>
<div class="paragraph">
<p>This task uses <a href="https://pandoc.org">pandoc</a> to convert the DocBook output from AsciiDoctor to ePub.
This way, you can read your documentation in a convenient way on an eBook-reader.</p>
</div>
<div class="paragraph">
<p>The resulting file can be found in <code>build/docs/epub</code></p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Blog-Post: <a href="https://rdmueller.github.io/create-epub/">Turn your Document into an Audio-Book</a>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_source_8">3.12.1. Source</h4>
<div class="listingblock">
<div class="title">pandoc.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">task convertToEpub (
        <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">docToolchain</span><span class="delimiter">'</span></span>,
        <span class="key">description</span>: <span class="string"><span class="delimiter">'</span><span class="content">converts file to .epub via pandoc. Needs pandoc installed.</span><span class="delimiter">'</span></span>,
        <span class="key">type</span>: Exec
) {
    <span class="comment">// All files with option `epub` in config.groovy is converted to docbook and then to epub.</span>
    <span class="keyword">def</span> sourceFilesEpub = sourceFiles.findAll { <span class="string"><span class="delimiter">'</span><span class="content">epub</span><span class="delimiter">'</span></span> <span class="keyword">in</span> <span class="local-variable">it</span>.formats }
    sourceFilesEpub.each {
        <span class="keyword">def</span> sourceFile = <span class="local-variable">it</span>.file.replace(<span class="string"><span class="delimiter">'</span><span class="content">.adoc</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">.xml</span><span class="delimiter">'</span></span>)
        <span class="keyword">def</span> targetFile = sourceFile.replace(<span class="string"><span class="delimiter">'</span><span class="content">.xml</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">.epub</span><span class="delimiter">'</span></span>)

        workingDir <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">$</span>targetDir</span><span class="content">/docbook</span><span class="delimiter">&quot;</span></span>
        executable = <span class="string"><span class="delimiter">&quot;</span><span class="content">pandoc</span><span class="delimiter">&quot;</span></span>
        args = [<span class="string"><span class="delimiter">'</span><span class="content">-r</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">docbook</span><span class="delimiter">'</span></span>,
                <span class="string"><span class="delimiter">'</span><span class="content">-t</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">epub</span><span class="delimiter">'</span></span>,
                <span class="string"><span class="delimiter">'</span><span class="content">-o</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">&quot;</span><span class="content">../epub/</span><span class="inline"><span class="inline-delimiter">$</span>targetFile</span><span class="delimiter">&quot;</span></span>,
                sourceFile]
    }
    doFirst {
        <span class="keyword">new</span> <span class="predefined-type">File</span>(<span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">$</span>targetDir</span><span class="content">/epub/</span><span class="delimiter">&quot;</span></span>).mkdirs()
    }
}</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect2">
<h3 id="_exportea">3.13. exportEA</h3>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
Currently this feature is WINDOWS-only. <a href="https://github.com/docToolchain/docToolchain/issues/231">See related issue</a>
</td>
</tr>
</table>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_exportEA.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_exportEA.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5f528a3f6814d28b583f31842e3e8d9e?d=identicon" alt="Alexander Schwartz " width="32" title="Alexander Schwartz "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/69124393772349d32fb9fe975545757d?d=identicon" alt="Lars Francke " width="32" title="Lars Francke "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/a8ff23c0e396f621ed233027f21c27ea?d=identicon" alt="Peter Stange " width="32" title="Peter Stange "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/61c257b76a94d988f747a87850b7e114?d=identicon" alt="ghp-dev " width="32" title="ghp-dev "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>1 minute to read</pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="images/ea/Manual/exportEA.png" alt="exportEA">
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Blog-Posts: <a href="https://rdmueller.github.io/jria2eac/">JIRA to Sparx EA</a>, <a href="https://rdmueller.github.io/sparx-ea/">Did you ever wish you had better Diagrams?</a>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_configuration_2">3.13.1. Configuration</h4>
<div class="paragraph">
<p>By default no special configuration is necessary.
But, to be more specific on the project and its packages to be used for export,
two optional parameter configurations are available.
The parameters can be used independently from each other.
A sample how to edit your projects Config.groovy is given in the 'Config.groovy'
of the docToolchain project itself.</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">connection</dt>
<dd>
<p>Set the connection to a certain project or comment it out to use all project files inside the src folder or its child folder.</p>
</dd>
<dt class="hdlist1">packageFilter</dt>
<dd>
<p>Add one or multiple packageGUIDs to be used for export. All packages are analysed, if no packageFilter is set.</p>
</dd>
</dl>
</div>
</div>
<div class="sect3">
<h4 id="_source_9">3.13.2. Source</h4>
<div class="listingblock">
<div class="title">build.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">task exportEA(
        <span class="key">dependsOn</span>: [streamingExecute],
        <span class="key">description</span>: <span class="string"><span class="delimiter">'</span><span class="content">exports all diagrams and some texts from EA files</span><span class="delimiter">'</span></span>,
        <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">docToolchain</span><span class="delimiter">'</span></span>
) {
    doFirst {
        logger.info (<span class="string"><span class="delimiter">&quot;</span><span class="content">docToolchain&gt; docDir: </span><span class="delimiter">&quot;</span></span>+docDir)
        <span class="comment">//make sure path for notes exists</span>
        <span class="comment">//and remove old notes</span>
        <span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/ea</span><span class="delimiter">'</span></span>).deleteDir()
        <span class="comment">//also remove old diagrams</span>
        <span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/images/ea</span><span class="delimiter">'</span></span>).deleteDir()
        <span class="comment">//create a readme to clarify things</span>
        <span class="keyword">def</span> readme = <span class="string"><span class="delimiter">&quot;&quot;&quot;</span><span class="content">This folder contains exported diagrams or notes from Enterprise Architect.

Please note that these are generated files but reside in the `src`-folder in order to be versioned.

This is to make sure that they can be used from environments other than windows.

# Warning!

**The contents of this folder        will be overwritten with each re-export!**

use `gradle exportEA` to re-export files
</span><span class="delimiter">&quot;&quot;&quot;</span></span>
        <span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/images/ea/.</span><span class="delimiter">'</span></span>).mkdirs()
        <span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/images/ea/readme.ad</span><span class="delimiter">'</span></span>).write(readme)
        <span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/ea/.</span><span class="delimiter">'</span></span>).mkdirs()
        <span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/ea/readme.ad</span><span class="delimiter">'</span></span>).write(readme)
    }
    doLast {
        logger.info(<span class="string"><span class="delimiter">&quot;</span><span class="content">docToolchain &gt; exportEA: </span><span class="delimiter">&quot;</span></span>+docDir)
        logger.info(<span class="string"><span class="delimiter">&quot;</span><span class="content">docToolchain &gt; exportEA: </span><span class="delimiter">&quot;</span></span>+mainConfigFile)
        <span class="keyword">def</span> configFile = <span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, mainConfigFile)
        <span class="keyword">def</span> config = <span class="keyword">new</span> ConfigSlurper().parse(configFile.text)
                <span class="keyword">def</span> scriptParameterString = <span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span>
        <span class="keyword">if</span>(!config.exportEA.connection.isEmpty()){
            logger.info(<span class="string"><span class="delimiter">&quot;</span><span class="content">docToolchain &gt; exportEA: found </span><span class="delimiter">&quot;</span></span>+config.exportEA.connection)
                        scriptParameterString = scriptParameterString + <span class="string"><span class="delimiter">&quot;</span><span class="content">-c </span><span class="char">\&quot;</span><span class="inline"><span class="inline-delimiter">${</span>config.exportEA.connection<span class="inline-delimiter">}</span></span><span class="char">\&quot;</span><span class="delimiter">&quot;</span></span>
        }
        <span class="keyword">if</span> (!config.exportEA.packageFilter.isEmpty()){
            <span class="keyword">def</span> packageFilterToCreate = config.exportEA.packageFilter <span class="keyword">as</span> <span class="predefined-type">List</span>
            logger.info(<span class="string"><span class="delimiter">&quot;</span><span class="content">docToolchain &gt; exportEA: package filter list size: </span><span class="delimiter">&quot;</span></span>+packageFilterToCreate.size())
            packageFilterToCreate.each { packageFilter -&gt;
               scriptParameterString = scriptParameterString + <span class="string"><span class="delimiter">&quot;</span><span class="content"> -p </span><span class="char">\&quot;</span><span class="inline"><span class="inline-delimiter">${</span>packageFilter<span class="inline-delimiter">}</span></span><span class="char">\&quot;</span><span class="delimiter">&quot;</span></span>
            }
        }
        <span class="comment">//execute through cscript in order to make sure that we get WScript.echo right</span>
        <span class="string"><span class="delimiter">&quot;</span><span class="content">%SystemRoot%</span><span class="char">\\</span><span class="content">System32</span><span class="char">\\</span><span class="content">cscript.exe //nologo </span><span class="inline"><span class="inline-delimiter">${</span>projectDir<span class="inline-delimiter">}</span></span><span class="content">/scripts/exportEAP.vbs </span><span class="inline"><span class="inline-delimiter">${</span>scriptParameterString<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>.executeCmd()
        <span class="comment">//the VB Script is only capable of writing iso-8859-1-Files.</span>
        <span class="comment">//we now have to convert them to UTF-8</span>
        <span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/ea/.</span><span class="delimiter">'</span></span>).eachFileRecurse { file -&gt;
            <span class="keyword">if</span> (file.isFile()) {
                println <span class="string"><span class="delimiter">&quot;</span><span class="content">exported notes </span><span class="delimiter">&quot;</span></span> + file.canonicalPath
                file.write(file.getText(<span class="string"><span class="delimiter">'</span><span class="content">iso-8859-1</span><span class="delimiter">'</span></span>), <span class="string"><span class="delimiter">'</span><span class="content">utf-8</span><span class="delimiter">'</span></span>)
            }
        }
    }
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">scripts/exportEAP.vbs</div>
<div class="content">
<pre class="CodeRay highlight"><code>    ' based on the "Project Interface Example" which comes with EA
    ' http://stackoverflow.com/questions/1441479/automated-method-to-export-enterprise-architect-diagrams

    Dim EAapp 'As EA.App
    Dim Repository 'As EA.Repository
    Dim FS 'As Scripting.FileSystemObject

    Dim projectInterface 'As EA.Project

    Const   ForAppending = 8

    ' Helper
    ' http://windowsitpro.com/windows/jsi-tip-10441-how-can-vbscript-create-multiple-folders-path-mkdir-command
    Function MakeDir (strPath)
      Dim strParentPath, objFSO
      Set objFSO = CreateObject("Scripting.FileSystemObject")
      On Error Resume Next
      strParentPath = objFSO.GetParentFolderName(strPath)

      If Not objFSO.FolderExists(strParentPath) Then MakeDir strParentPath
      If Not objFSO.FolderExists(strPath) Then objFSO.CreateFolder strPath
      On Error Goto 0
      MakeDir = objFSO.FolderExists(strPath)

    End Function

    Sub WriteNote(currentModel, currentElement, notes, prefix)
        If (Left(notes, 6) = "{adoc:") Then
            strFileName = Mid(notes,7,InStr(notes,"}")-7)
            strNotes = Right(notes,Len(notes)-InStr(notes,"}"))
            set objFSO = CreateObject("Scripting.FileSystemObject")
            If (currentModel.Name="Model") Then
              ' When we work with the default model, we don't need a sub directory
              path = "./src/docs/ea/"
            Else
              path = "./src/docs/ea/"&amp;currentModel.Name&amp;"/"
            End If
            MakeDir(path)
            ' WScript.echo path&amp;strFileName
            post = ""
            If (prefix&lt;&gt;"") Then
                post = "_"
            End If
            MakeDir(path&amp;prefix&amp;post)
            set objFile = objFSO.OpenTextFile(path&amp;prefix&amp;post&amp;strFileName&amp;".ad",ForAppending, True)
            name = currentElement.Name
            name = Replace(name,vbCr,"")
            name = Replace(name,vbLf,"")
            ' WScript.echo "-"&amp;Left(strNotes, 6)&amp;"-"
            if (Left(strNotes, 3) = vbCRLF&amp;"|") Then
                ' content should be rendered as table - so don't interfere with it
                objFile.WriteLine(vbCRLF)
            else
                'let's add the name of the object
                objFile.WriteLine(vbCRLF&amp;vbCRLF&amp;"."&amp;name)
            End If
            objFile.WriteLine(vbCRLF&amp;strNotes)
            objFile.Close
            if (prefix&lt;&gt;"") Then
                ' write the same to a second file
                set objFile = objFSO.OpenTextFile(path&amp;prefix&amp;".ad",ForAppending, True)
                objFile.WriteLine(vbCRLF&amp;vbCRLF&amp;"."&amp;name&amp;vbCRLF&amp;strNotes)
                objFile.Close
            End If
        End If
    End Sub

    Sub SyncJira(currentModel, currentDiagram)
        notes = currentDiagram.notes
        set currentPackage = Repository.GetPackageByID(currentDiagram.PackageID)
        updated = 0
        created = 0
        If (Left(notes, 6) = "{jira:") Then
            WScript.echo " &gt;&gt;&gt;&gt; Diagram jira tag found"
            strSearch = Mid(notes,7,InStr(notes,"}")-7)
            Set objShell = CreateObject("WScript.Shell")
            'objShell.CurrentDirectory = fso.GetFolder("./scripts")
            Set objExecObject = objShell.Exec ("cmd /K  groovy ./scripts/exportJira.groovy """ &amp; strSearch &amp;""" &amp; exit")
            strReturn = ""
            x = 0
            y = 0
            Do While Not objExecObject.StdOut.AtEndOfStream
                output = objExecObject.StdOut.ReadLine()
                ' WScript.echo output
                jiraElement = Split(output,"|")
                name = jiraElement(0)&amp;":"&amp;vbCR&amp;vbLF&amp;jiraElement(4)
                On Error Resume Next
                Set requirement = currentPackage.Elements.GetByName(name)
                On Error Goto 0
                if (IsObject(requirement)) then
                    ' element already exists
                    requirement.notes = ""
                    requirement.notes = requirement.notes&amp;"&lt;a href='"&amp;jiraElement(5)&amp;"'&gt;"&amp;jiraElement(0)&amp;"&lt;/a&gt;"&amp;vbCR&amp;vbLF
                    requirement.notes = requirement.notes&amp;"Priority: "&amp;jiraElement(1)&amp;vbCR&amp;vbLF
                    requirement.notes = requirement.notes&amp;"Created: "&amp;jiraElement(2)&amp;vbCR&amp;vbLF
                    requirement.notes = requirement.notes&amp;"Assignee: "&amp;jiraElement(3)&amp;vbCR&amp;vbLF
                    requirement.Update()
                    updated = updated + 1
                else
                    Set requirement = currentPackage.Elements.AddNew(name,"Requirement")
                    requirement.notes = ""
                    requirement.notes = requirement.notes&amp;"&lt;a href='"&amp;jiraElement(5)&amp;"'&gt;"&amp;jiraElement(0)&amp;"&lt;/a&gt;"&amp;vbCR&amp;vbLF
                    requirement.notes = requirement.notes&amp;"Priority: "&amp;jiraElement(1)&amp;vbCR&amp;vbLF
                    requirement.notes = requirement.notes&amp;"Created: "&amp;jiraElement(2)&amp;vbCR&amp;vbLF
                    requirement.notes = requirement.notes&amp;"Assignee: "&amp;jiraElement(3)&amp;vbCR&amp;vbLF
                    requirement.Update()
                    currentPackage.Elements.Refresh()
                    Set dia_obj = currentDiagram.DiagramObjects.AddNew("l="&amp;(10+x*200)&amp;";t="&amp;(10+y*50)&amp;";b="&amp;(10+y*50+44)&amp;";r="&amp;(10+x*200+180),"")
                    x = x + 1
                    if (x&gt;3) then
                      x = 0
                      y = y + 1
                    end if
                    dia_obj.ElementID = requirement.ElementID
                    dia_obj.Update()
                    created = created + 1
                end if
            Loop
            Set objShell = Nothing
            WScript.echo "created "&amp;created&amp;" requirements"
            WScript.echo "updated "&amp;updated&amp;" requirements"
        End If
    End Sub

    Sub SaveDiagram(currentModel, currentDiagram)
        ' Open the diagram
        Repository.OpenDiagram(currentDiagram.DiagramID)

        ' Save and close the diagram
        If (currentModel.Name="Model") Then
            ' When we work with the default model, we don't need a sub directory
            path = "/src/docs/images/ea/"
        Else
            path = "/src/docs/images/ea/" &amp; currentModel.Name &amp; "/"
        End If
        diagramName = Replace(currentDiagram.Name," ","_")
        diagramName = Replace(diagramName,vbCr,"")
        diagramName = Replace(diagramName,vbLf,"")
        filename = path &amp; diagramName &amp; ".png"
        MakeDir("." &amp; path)
        projectInterface.SaveDiagramImageToFile(fso.GetAbsolutePathName(".")&amp;filename)
        ' projectInterface.putDiagramImageToFile currentDiagram.DiagramID,fso.GetAbsolutePathName(".")&amp;filename,1
        WScript.echo " extracted image to ." &amp; filename
        Repository.CloseDiagram(currentDiagram.DiagramID)
        For Each diagramElement In currentDiagram.DiagramObjects
            Set currentElement = Repository.GetElementByID(diagramElement.ElementID)
            WriteNote currentModel, currentElement, currentElement.Notes, diagramName&amp;"_notes"
        Next
        For Each diagramLink In currentDiagram.DiagramLinks
            set currentConnector = Repository.GetConnectorByID(diagramLink.ConnectorID)
            WriteNote currentModel, currentConnector, currentConnector.Notes, diagramName&amp;"_links"
        Next
    End Sub
    '
    ' Recursively saves all diagrams under the provided package and its children
    '
    Sub DumpDiagrams(thePackage,currentModel)
        Set currentPackage = thePackage

        ' export element notes
        For Each currentElement In currentPackage.Elements
            WriteNote currentModel, currentElement, currentElement.Notes, ""
            ' export connector notes
            For Each currentConnector In currentElement.Connectors
                ' WScript.echo currentConnector.ConnectorGUID
                if (currentConnector.ClientID=currentElement.ElementID) Then
                    WriteNote currentModel, currentConnector, currentConnector.Notes, ""
                End If
            Next
            if (Not currentElement.CompositeDiagram Is Nothing) Then
                SyncJira currentModel, currentElement.CompositeDiagram
                SaveDiagram currentModel, currentElement.CompositeDiagram
            End If
            if (Not currentElement.Elements Is Nothing) Then
                DumpDiagrams currentElement,currentModel
            End If
        Next


        ' Iterate through all diagrams in the current package
        For Each currentDiagram In currentPackage.Diagrams
            SyncJira currentModel, currentDiagram
            SaveDiagram currentModel, currentDiagram
        Next

        ' Process child packages
        Dim childPackage 'as EA.Package
        ' otPackage = 5
        if (currentPackage.ObjectType = 5) Then
            For Each childPackage In currentPackage.Packages
                call DumpDiagrams(childPackage, currentModel)
            Next
        End If
    End Sub

    Function SearchEAProjects(path)
      For Each folder In path.SubFolders
        SearchEAProjects folder
      Next

      For Each file In path.Files
            If fso.GetExtensionName (file.Path) = "eap" OR fso.GetExtensionName (file.Path) = "eapx" Then
                WScript.echo "found "&amp;file.path
                If (Left(file.name, 1) = "_") Then
                    WScript.echo "skipping, because it start with `_` (replication)"
                Else
                    OpenProject(file.Path)
                End If
            End If
      Next
    End Function

    'Gets the package object as referenced by its GUID from the Enterprise Architect project.
    'Looks for the model node, the package is a child of as it is required for the diagram export.
    'Calls the Sub routine DumpDiagrams for the model and package found.
    'An error is printed to console only if the packageGUID is not found in the project.
    Function DumpPackageDiagrams(EAapp, packageGUID)
        WScript.echo "DumpPackageDiagrams"
        WScript.echo packageGUID
        Dim package
        Set package = EAapp.Repository.GetPackageByGuid(packageGUID)
        If (package Is Nothing) Then
          WScript.echo "invalid package - as package is not part of the project"
        Else
          Dim currentModel
          Set currentModel = package
          while currentModel.IsModel = false
            Set currentModel = EAapp.Repository.GetPackageByID(currentModel.parentID)
          wend
          ' Iterate through all child packages and save out their diagrams
          ' save all diagrams of package itself
          call DumpDiagrams(package, currentModel)
        End If
    End Function

    Sub OpenProject(file)
      ' open Enterprise Architect
      Set EAapp = CreateObject("EA.App")
      WScript.echo "opening Enterprise Architect. This might take a moment..."
      ' load project
      EAapp.Repository.OpenFile(file)
      ' make Enterprise Architect to not appear on screen
      EAapp.Visible = False

      ' get repository object
      Set Repository = EAapp.Repository
      ' Show the script output window
      ' Repository.EnsureOutputVisible("Script")

      Set projectInterface = Repository.GetProjectInterface()

      Dim childPackage 'As EA.Package
      ' Iterate through all model nodes
      Dim currentModel 'As EA.Package
      If (InStrRev(file,"{") &gt; 0) Then
        ' the filename references a GUID
        ' like {04C44F80-8DA1-4a6f-ECB8-982349872349}
        WScript.echo file
        GUID = Mid(file, InStrRev(file,"{")+0,38)
        WScript.echo GUID
        ' Iterate through all child packages and save out their diagrams
        call DumpPackageDiagrams(EAapp, GUID)
      Else
        If packageFilter.Count = 0 Then
          WScript.echo "done"
        ' Iterate through all model nodes
        For Each currentModel In Repository.Models
            ' Iterate through all child packages and save out their diagrams
            For Each childPackage In currentModel.Packages
                call DumpDiagrams(childPackage,currentModel)
            Next
        Next
        Else
          ' Iterate through all packages found in the package filter given by script parameter.
          For Each packageGUID In packageFilter
            call DumpPackageDiagrams(EAapp, packageGUID)
          Next
        End If
      End If
      EAapp.Repository.CloseFile()
    End Sub

  Private connectionString
  Private packageFilter
  Set packageFilter = CreateObject("System.Collections.ArrayList")
  Set objArguments = WScript.Arguments
  Dim argCount
  argCount = 0
  While objArguments.Count &gt; argCount+1
    Select Case objArguments(argCount)
      Case "-c"
        connectionString = objArguments(argCount+1)
      Case "-p"
        packageFilter.Add objArguments(argCount+1)
    End Select
    argCount = argCount + 2
  WEnd
  set fso = CreateObject("Scripting.fileSystemObject")
  WScript.echo "Image extractor"
  If IsEmpty(connectionString) Then
  WScript.echo "looking for .eap(x) files in " &amp; fso.GetAbsolutePathName(".") &amp; "/src"
  'Dim f As Scripting.Files
  SearchEAProjects fso.GetFolder("./src")
  Else
     WScript.echo "opening database connection now"
     OpenProject(connectionString)
  End If
  WScript.echo "finished exporting images"</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect2">
<h3 id="_exportvisio">3.14. exportVisio</h3>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_exportVisio.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_exportVisio.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5f528a3f6814d28b583f31842e3e8d9e?d=identicon" alt="Alexander Schwartz " width="32" title="Alexander Schwartz "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/69124393772349d32fb9fe975545757d?d=identicon" alt="Lars Francke " width="32" title="Lars Francke "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/2e3af23bd872d2b971788a4519e3bf46?d=identicon" alt="jakubjab " width="32" title="jakubjab "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>1 minute to read</pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="images/ea/Manual/exportVisio.png" alt="exportVisio">
</div>
</div>
<div class="paragraph">
<p>This tasks searches for Visio files in the <code>/src/docs</code> folder.
It then exports all diagrams and element notes to <code>/src/docs/images/visio</code> and <code>/src/docs/visio</code>.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Images are stored as <code>/images/visio/[filename]-[pagename].png</code></p>
</li>
<li>
<p>Notes are stored as <code>/visio/[filename]-[pagename].adoc</code></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>You can specify a file name to which the notes of a diagram are exported by starting any comment with <code>{adoc:[filename].adoc}</code>.
It will then be written to <code>/visio/[filename].adoc</code>.</p>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
Currently, only Visio files stored directly in <code>/src/docs</code> are supported.
For all others, the exported files will be in the wrong location.
</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Please close any running Visio instance before starting this task.
</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Todos: <a href="https://github.com/docToolchain/docToolchain/issues/112">issue #112</a>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_source_10">3.14.1. Source</h4>
<div class="listingblock">
<div class="title">exportVisio.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">task exportVisio(
        <span class="key">dependsOn</span>: [streamingExecute],
        <span class="key">description</span>: <span class="string"><span class="delimiter">'</span><span class="content">exports all diagrams and notes from visio files</span><span class="delimiter">'</span></span>,
        <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">docToolchain</span><span class="delimiter">'</span></span>
) {
    doLast {
        <span class="comment">//make sure path for notes exists</span>
        <span class="comment">//and remove old notes</span>
        <span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/visio</span><span class="delimiter">'</span></span>).deleteDir()
        <span class="comment">//also remove old diagrams</span>
        <span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/images/visio</span><span class="delimiter">'</span></span>).deleteDir()
        <span class="comment">//create a readme to clarify things</span>
        <span class="keyword">def</span> readme = <span class="string"><span class="delimiter">&quot;&quot;&quot;</span><span class="content">This folder contains exported diagrams and notes from visio files.

Please note that these are generated files but reside in the `src`-folder in order to be versioned.

This is to make sure that they can be used from environments other than windows.

# Warning!

**The contents of this folder        will be overwritten with each re-export!**

use `gradle exportVisio` to re-export files
</span><span class="delimiter">&quot;&quot;&quot;</span></span>
        <span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/images/visio/.</span><span class="delimiter">'</span></span>).mkdirs()
        <span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/images/visio/readme.ad</span><span class="delimiter">'</span></span>).write(readme)
        <span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/visio/.</span><span class="delimiter">'</span></span>).mkdirs()
        <span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/visio/readme.ad</span><span class="delimiter">'</span></span>).write(readme)
        <span class="keyword">def</span> sourcePath = <span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/.</span><span class="delimiter">'</span></span>).canonicalPath
        <span class="keyword">def</span> scriptPath = <span class="keyword">new</span> <span class="predefined-type">File</span>(projectDir, <span class="string"><span class="delimiter">'</span><span class="content">scripts/VisioPageToPngConverter.ps1</span><span class="delimiter">'</span></span>).canonicalPath
        <span class="string"><span class="delimiter">&quot;</span><span class="content">powershell </span><span class="inline"><span class="inline-delimiter">${</span>scriptPath<span class="inline-delimiter">}</span></span><span class="content"> -SourcePath </span><span class="inline"><span class="inline-delimiter">${</span>sourcePath<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>.executeCmd()
    }
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">scripts/VisioPageToPngConverter.ps1</div>
<div class="content">
<pre class="CodeRay highlight"><code># Convert all pages in all visio files in the given directory to png files.
# A Visio windows might flash shortly.
# The converted png files are stored in the same directory
# The name of the png file is concatenated from the Visio file name and the page name.
# In addtion all the comments are stored in adoc files.
# If the Viso file is named "MyVisio.vsdx" and the page is called "FirstPage"
# the name of the png file will be "MyVisio-FirstPage.png" and the comment will
# be stored in "MyVisio-FirstPage.adoc".
# But for the name of the adoc files there is an alternative. It can be given in the first
# line of the comment. If it is given in the comment it has to be given in curly brackes
# with the prefix "adoc:", e.g. {adoc:MyCommentFile.adoc}
# Prerequisites: Viso and PowerShell has to be installed on the computer.
# Parameter: SourcePath where visio files can be found
# Example powershell VisoPageToPngConverter.ps1 -SourcePath c:\convertertest\

Param
(
    [Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=0)]
    [Alias('p')][String]$SourcePath
)

Write-Output "starting to export visio"

If (!(Test-Path -Path $SourcePath))
{
    Write-Warning "The path ""$SourcePath"" does not exist or is not accessible, please input the correct path."
    Exit
}

# Extend the source path to get only Visio files of the given directory and not in subdircetories
If ($SourcePath.EndsWith("\"))
{
    $SourcePath  = "$SourcePath"
}
Else
{
    $SourcePath  = "$SourcePath\"
}

$VisioFiles = Get-ChildItem -Path "$SourcePath*" -Recurse -Include *.vsdx,*.vssx,*.vstx,*.vxdm,*.vssm,*.vstm,*.vsd,*.vdw,*.vss,*.vst

If(!($VisioFiles))
{
    Write-Warning "There are no Visio files in the path ""$SourcePath""."
    Exit
}

$VisioApp = New-Object -ComObject Visio.Application
$VisioApp.Visible = $false

# Extract the png from all the files in the folder
Foreach($File in $VisioFiles)
{
    $FilePath = $File.FullName
    Write-Output "found ""$FilePath"" ."
    $FileDirectory = $File.DirectoryName   # Get the folder containing the Visio file. Will be used to store the png and adoc files
    $FileBaseName = $File.BaseName -replace '[ :/\\*?|&lt;&gt;]','-'   # Get the filename to be used as part of the name of the png and adoc files

    Try
    {
        $Document = $VisioApp.Documents.Open($FilePath)
        $Pages = $VisioApp.ActiveDocument.Pages
        Foreach($Page in $Pages)
        {
            # Create valid filenames for the png and adoc files
            $PngFileName = $Page.Name -replace '[ :/\\*?|&lt;&gt;]','-'
            $PngFileName = "$FileBaseName-$PngFileName.png"
            $AdocFileName = $PngFileName.Replace(".png", ".adoc")

            #TODO: this needs better logic
            Write-Output("$SourcePath\images\visio\$PngFileName")
            $Page.Export("$SourcePath\images\visio\$PngFileName")

            $AllPageComments = ""
            ForEach($PageComment in $Page.Comments)
            {
                # Extract adoc filename from comment text if the syntax is valid
                # Remove the filename from the text and save the comment in a file with a valid name
                $EofStringIndex = $PageComment.Text.IndexOf(".adoc}")
                if ($PageComment.Text.StartsWith("{adoc") -And ($EofStringIndex -gt 6))
                {
                    $AdocFileName = $PageComment.Text.Substring(6, $EofStringIndex -1)
                    $AllPageComments += $PageComment.Text.Substring($EofStringIndex + 6)
                }
                else
                {
                    $AllPageComments += $PageComment.Text+"`n"
                }
            }
            If ($AllPageComments)
            {

                $AdocFileName = $AdocFileName -replace '[:/\\*?|&lt;&gt;]','-'
                #TODO: this needs better logic
                $stream = [System.IO.StreamWriter] "$SourcePath\visio\$AdocFileName"
                $stream.WriteLine($AllPageComments)
                $stream.close()
            }
        }
        $Document.Close()
    }
    Catch
    {
        if ($Document)
        {
            $Document.Close()
        }
        Write-Warning "One or more visio page(s) in file ""$FilePath"" have been lost in this converting."
        Write-Warning "Error was: $_"
    }
}
$VisioApp.Quit()</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect2">
<h3 id="_exportdrawio">3.15. exportDrawIo</h3>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_exportDrawIo.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_exportDrawIo.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Müller " width="32" title="Ralf D. Müller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/3f7fa472f24ae9ea4caaefd3a669f6ea?d=identicon" alt="Jared Morgan " width="32" title="Jared Morgan "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5f528a3f6814d28b583f31842e3e8d9e?d=identicon" alt="Alexander Schwartz " width="32" title="Alexander Schwartz "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>2 minutes to read</pre>
</div>
</div>
<div class="paragraph">
<p>There is no <code>exportDrawIo</code> task because it is not required.
You can continue to use <a href="https://draw.io">draw.io</a> as an editor for your diagrams by making a change to your diagram authoring workflow.</p>
</div>
<div class="paragraph">
<p>Export your draw.io diagrams as a PNG with the source embedded in the file metadata.
Using this approach allows you to embed your diagrams into AsciiDoc source as you normally do with the <code>image::</code> macro, with the added advantage of storing the diagram source with the image itself.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
If you are converting a Confluence page with embedded draw.io diagrams to AsciiDoc, you can use this export workflow to continue to enjoy the draw.io editing experience.
</td>
</tr>
</table>
</div>
<div class="olist arabic">
<div class="title">Export an editable PNG diagram from Confluence</div>
<ol class="arabic">
<li>
<p>Load the diagram you want to export from Confluence.</p>
</li>
<li>
<p>Click <span class="menuseq"><b class="menu">File</b>&#160;<i class="fa fa-angle-right caret"></i> <b class="submenu">Export as</b>&#160;<i class="fa fa-angle-right caret"></i> <b class="menuitem">PNG&#8230;&#8203;</b></span></p>
</li>
<li>
<p>In the Image modal, make sure that "Include a copy of my diagram" is selected.</p>
</li>
<li>
<p>Click Export to save the PNG file with the pattern <code>[file].dio.png</code>.</p>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Specifying <code>.dio</code> (short for "<strong>d</strong>raw<strong>io</strong>") in the name will help you identify PNG files containing embedded XML diagram source.
</td>
</tr>
</table>
</div>
</li>
<li>
<p>Commit the exported PNG file to source control.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>You now have a diagram that can be managed in source control, added to your documentation source, and edited using a draw.io Desktop version.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Draw.io offer free and open source desktop editors for all major operating system platforms.
See <a href="https://about.draw.io/integrations/" class="bare">https://about.draw.io/integrations/</a> to find a desktop editor application compatible with your operating system.
When you use the desktop version, just create your diagram with the <code>.png</code> or even better <code>.dio.png</code> extension and draw.io will always save your diagam as PNG with the source as meta data.
</td>
</tr>
</table>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
<div class="sect2">
<h3 id="_exportchangelog">3.16. exportChangeLog</h3>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_exportChangeLog.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_exportChangeLog.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/d2c684ec9e55edec506308356b67016?d=identicon" alt="Jakub J Jablonski " width="32" title="Jakub J Jablonski "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/69124393772349d32fb9fe975545757d?d=identicon" alt="Lars Francke " width="32" title="Lars Francke "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>2 minutes to read</pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="images/ea/Manual/exportChangeLog.png" alt="exportChangeLog">
</div>
</div>
<div class="paragraph">
<p>As the name says, this task exports the changelog to be referenced from within your documentation - if needed.
The changelog is written to <code>build/docs/changelog.adoc</code>.</p>
</div>
<div class="paragraph">
<p>This task can be configured to use different source control system or different directory.
To configure the task, copy <code>template_config/scripts/ChangelogConfig.groovy</code> to your directory and modify to your needs.
Then give the path to your configuration file to the task using -PchangelogConfigFile=&lt;your config file&gt;.
See the description inside the template for more details.</p>
</div>
<div class="paragraph">
<p>By default, the source is the Git changelog for the path <code>src/docs</code> - it only contains the commit messages for changes on the documentation.
All changes on the build or other sources from the repository will not show up.
By default, the changelog contains the changes with <em>date</em>, <em>author</em> and <em>commit message</em> already formatted as AsciiDoc table content:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>| 09.04.2017
| Ralf D. Mueller
| fix #24 template updated to V7.0

| 08.04.2017
| Ralf D. Mueller
| fixed typo</pre>
</div>
</div>
<div class="paragraph">
<p>You simply include it like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>.Changes
[options="header",cols="1,2,6"]
|====
| Date
| Author
| Comment

include::../../build/docs/changelog.adoc[]

|====</pre>
</div>
</div>
<div class="paragraph">
<p>By excluding the table definition, you can easily translate the table headings through different text snippets.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
it might make sense to only include certain commit messages from the change log or exclude others (starting with <code>#</code> or <code>//</code>?).
But this isn&#8217;t implemented yet.
</td>
</tr>
</table>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Blog-Post: <a href="https://rdmueller.github.io/changelog/">The only constant in life is change</a>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_source_11">3.16.1. Source</h4>
<div class="listingblock">
<div class="title">exportChangelog.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">task exportChangeLog(
        <span class="key">description</span>: <span class="string"><span class="delimiter">'</span><span class="content">exports the change log from a git subpath</span><span class="delimiter">'</span></span>,
        <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">docToolchain</span><span class="delimiter">'</span></span>
) {
    doFirst {
        <span class="keyword">new</span> <span class="predefined-type">File</span>(targetDir).mkdirs()
    }
    doLast {
        logger.info(<span class="string"><span class="delimiter">&quot;</span><span class="content">docToolchain&gt; docDir: </span><span class="delimiter">&quot;</span></span>+docDir)
        logger.info(<span class="string"><span class="delimiter">&quot;</span><span class="content">docToolchain&gt; mainConfigFile: </span><span class="delimiter">&quot;</span></span>+mainConfigFile)
        <span class="keyword">def</span> config = <span class="keyword">new</span> ConfigSlurper().parse(<span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, mainConfigFile).text)

        <span class="keyword">def</span> cmd = <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">${</span>config.changelog.cmd<span class="inline-delimiter">}</span></span><span class="content"> .</span><span class="delimiter">&quot;</span></span>
        <span class="keyword">def</span> changes = cmd.execute(<span class="predefined-constant">null</span>, <span class="keyword">new</span> <span class="predefined-type">File</span>(docDir, config.changelog.dir)).text
        <span class="keyword">def</span> changelog = <span class="keyword">new</span> <span class="predefined-type">File</span>(targetDir, <span class="string"><span class="delimiter">'</span><span class="content">changelog.adoc</span><span class="delimiter">'</span></span>)
        logger.info <span class="string"><span class="delimiter">&quot;</span><span class="content">&gt; changelog exported </span><span class="inline"><span class="inline-delimiter">${</span>changelog.canonicalPath<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>
        changelog.write(changes)
    }
}</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect2">
<h3 id="_exportcontributors">3.17. exportContributors</h3>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_exportContributors.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_exportContributors.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5f528a3f6814d28b583f31842e3e8d9e?d=identicon" alt="Alexander Schwartz " width="32" title="Alexander Schwartz "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>2 minutes to read</pre>
</div>
</div>
<div class="paragraph">
<p>This tasks crawls through all Asciidoctor source files and extracts a list of contributors.
This list is then rendered as AsciiDoc images of the contributor&#8217;s gravatar picture.</p>
</div>
<div class="paragraph">
<p>The extracted lists are stored in <code>/workspace/docToolchain/build/docs/contributors/manual/03_task_exportContributors.adoc</code> to be easily included in your documents.</p>
</div>
<div class="paragraph">
<p>Best way to use this feature is to create a <code>feedback.adoc</code> file which might look like this:</p>
</div>
<div class="listingblock">
<div class="title">feedback.adoc</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="asciidoc">ifndef::backend-pdf[] <i class="conum" data-value="1"></i><b>(1)</b>

    image::https://img.shields.io/badge/improve-this%20doc-orange.svg[link={manualdir}{filename}, float=right] <i class="conum" data-value="2"></i><b>(2)</b>
    image::https://img.shields.io/badge/create-an%20issue-blue.svg[link=&quot;https://github.com/docToolchain/documentation/issues/new?title=&amp;body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:{filename}&quot;, float=right] <i class="conum" data-value="3"></i><b>(3)</b>

endif::[]

include::{targetDir}/contributors/{filename}[] <i class="conum" data-value="4"></i><b>(4)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>do not show this section when rendered as PDF</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>create an "improve this doc" button which links to the git sources</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>create a "create-an-issue" button which links to your issue tracker</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>include the list of contributors created by this task</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_reading_time">3.17.1. Reading Time</h4>
<div class="paragraph">
<p>The task also appends to the list of contributors the estimated reading time.</p>
</div>
</div>
<div class="sect3">
<h4 id="_file_attributes">3.17.2. File Attributes</h4>
<div class="paragraph">
<p>The task now also exports some attributes of the git files.
The extracted attributes are stored in <code>/workspace/docToolchain/build/docs/fileattribs/manual/03_task_exportContributors.adoc</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="asciidoc">:lastUpdated: 16.05.2019 06:22
:lastAuthorName: Ralf D. Müller
:lastAuthorEmail: ralf.d.mueller@gmail.com
:lastAuthorAvatar: http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon[32,32,role='gravatar',alt='Ralf D. Müller',title='Ralf D. Müller']
:lastMessage: #310 started to document config options</code></pre>
</div>
</div>
<div class="paragraph">
<p>This enables you to import these attributes the same way you import the contributors list and use these attributes.</p>
</div>
<div class="paragraph">
<p>Here is an example:</p>
</div>
<div class="listingblock">
<div class="title">feedback.adoc</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="asciidoc">include::{targetDir}/fileattribs/{filename}[]

Last updated {lastUpdated} by {lastAuthorName}</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect2">
<h3 id="_exportjiraissues">3.18. exportJiraIssues</h3>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_exportJiraIssues.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_exportJiraIssues.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5f528a3f6814d28b583f31842e3e8d9e?d=identicon" alt="Alexander Schwartz " width="32" title="Alexander Schwartz "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>1 minute to read</pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="images/ea/Manual/exportJiraIssues.png" alt="exportJiraIssues">
</div>
</div>
<div class="paragraph">
<p>This task exports all issues for a given query from jira as AsciiDoc table.</p>
</div>
<div class="paragraph">
<p>The configuration for this task can currently be found within <code>gradle.properties</code>.
Username/Password is deprecated, so you have to use username/API-token instead.
An API-Token can be created through <a href="https://id.atlassian.com/manage/api-tokens" class="bare">https://id.atlassian.com/manage/api-tokens</a>.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Blog-Post: <a href="https://rdmueller.github.io/Jira/">Living Documents for Agile Projects</a>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_source_12">3.18.1. Source</h4>
<div class="listingblock">
<div class="title">exportJiraIssues.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">task exportJiraIssues(
        <span class="key">description</span>: <span class="string"><span class="delimiter">'</span><span class="content">exports all jira issues from a given search</span><span class="delimiter">'</span></span>,
        <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">docToolchain</span><span class="delimiter">'</span></span>
) {
    doLast {
        <span class="keyword">def</span> user = jiraUser
        <span class="keyword">def</span> pass = jiraPass
        <span class="keyword">if</span> (!pass) {
            pass = <span class="predefined-type">System</span>.console().readPassword(<span class="string"><span class="delimiter">&quot;</span><span class="content">Jira password for user '</span><span class="inline"><span class="inline-delimiter">$</span>user</span><span class="content">': </span><span class="delimiter">&quot;</span></span>)
        }

        <span class="keyword">def</span> stats = [:]
        <span class="keyword">def</span> jira = <span class="keyword">new</span> groovyx.net.http.RESTClient(jiraRoot + <span class="string"><span class="delimiter">'</span><span class="content">/rest/api/2/</span><span class="delimiter">'</span></span>)
        jira.encoderRegistry = <span class="keyword">new</span> groovyx.net.http.EncoderRegistry(<span class="key">charset</span>: <span class="string"><span class="delimiter">'</span><span class="content">utf-8</span><span class="delimiter">'</span></span>)
        <span class="keyword">def</span> headers = [
                <span class="string"><span class="delimiter">'</span><span class="content">Authorization</span><span class="delimiter">'</span></span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">Basic </span><span class="delimiter">&quot;</span></span> + <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">${</span>user<span class="inline-delimiter">}</span></span><span class="content">:</span><span class="inline"><span class="inline-delimiter">${</span>pass<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>.bytes.encodeBase64().toString(),
                <span class="string"><span class="delimiter">'</span><span class="content">Content-Type</span><span class="delimiter">'</span></span> : <span class="string"><span class="delimiter">'</span><span class="content">application/json; charset=utf-8</span><span class="delimiter">'</span></span>
        ]
        <span class="keyword">def</span> openIssues = <span class="keyword">new</span> <span class="predefined-type">File</span>(targetDir, <span class="string"><span class="delimiter">'</span><span class="content">openissues.adoc</span><span class="delimiter">'</span></span>)
        openIssues.write(<span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">utf-8</span><span class="delimiter">'</span></span>)
        println jiraJql.replaceAll(<span class="string"><span class="delimiter">'</span><span class="content">%jiraProject%</span><span class="delimiter">'</span></span>, jiraProject).replaceAll(<span class="string"><span class="delimiter">'</span><span class="content">%jiraLabel%</span><span class="delimiter">'</span></span>, jiraLabel)
        jira.get(<span class="key">path</span>: <span class="string"><span class="delimiter">'</span><span class="content">search</span><span class="delimiter">'</span></span>,
                <span class="key">query</span>: [<span class="string"><span class="delimiter">'</span><span class="content">jql</span><span class="delimiter">'</span></span>       : jiraJql.replaceAll(<span class="string"><span class="delimiter">'</span><span class="content">%jiraProject%</span><span class="delimiter">'</span></span>, jiraProject).replaceAll(<span class="string"><span class="delimiter">'</span><span class="content">%jiraLabel%</span><span class="delimiter">'</span></span>, jiraLabel),
                        <span class="string"><span class="delimiter">'</span><span class="content">maxResults</span><span class="delimiter">'</span></span>: <span class="integer">1000</span>,
                        <span class="string"><span class="delimiter">'</span><span class="content">fields</span><span class="delimiter">'</span></span>    : <span class="string"><span class="delimiter">'</span><span class="content">created,resolutiondate,priority,summary,timeoriginalestimate, assignee</span><span class="delimiter">'</span></span>
                ],
                <span class="key">headers</span>: headers
        ).data.issues.each { issue -&gt;
            openIssues.append(<span class="string"><span class="delimiter">&quot;</span><span class="content">| &lt;&lt;</span><span class="inline"><span class="inline-delimiter">${</span>issue.key<span class="inline-delimiter">}</span></span><span class="content">&gt;&gt; </span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">utf-8</span><span class="delimiter">'</span></span>)
            openIssues.append(<span class="string"><span class="delimiter">&quot;</span><span class="content">| </span><span class="inline"><span class="inline-delimiter">${</span>issue.fields.priority.name<span class="inline-delimiter">}</span></span><span class="content"> </span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">utf-8</span><span class="delimiter">'</span></span>)
            openIssues.append(<span class="string"><span class="delimiter">&quot;</span><span class="content">| </span><span class="inline"><span class="inline-delimiter">${</span><span class="predefined-type">Date</span>.parse(jiraDateTimeFormatParse, issue.fields.created).format(jiraDateTimeOutput)<span class="inline-delimiter">}</span></span><span class="content"> </span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">utf-8</span><span class="delimiter">'</span></span>)
            openIssues.append(<span class="string"><span class="delimiter">&quot;</span><span class="content">| </span><span class="inline"><span class="inline-delimiter">${</span>issue.fields.assignee ? issue.fields.assignee.displayName : <span class="string"><span class="delimiter">'</span><span class="content">not assigned</span><span class="delimiter">'</span></span><span class="inline-delimiter">}</span></span><span class="content"> </span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">utf-8</span><span class="delimiter">'</span></span>)
            openIssues.append(<span class="string"><span class="delimiter">&quot;</span><span class="content">| </span><span class="inline"><span class="inline-delimiter">${</span>jiraRoot<span class="inline-delimiter">}</span></span><span class="content">/browse/</span><span class="inline"><span class="inline-delimiter">${</span>issue.key<span class="inline-delimiter">}</span></span><span class="content">[</span><span class="inline"><span class="inline-delimiter">${</span>issue.fields.summary<span class="inline-delimiter">}</span></span><span class="content">]</span><span class="char">\n</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">utf-8</span><span class="delimiter">'</span></span>)
        }
    }
}</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect2">
<h3 id="_exportppt">3.19. exportPPT</h3>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
Currently this feature is WINDOWS-only. <a href="https://github.com/docToolchain/docToolchain/issues/231">See related issue</a>
</td>
</tr>
</table>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_exportPPT.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_exportPPT.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/61c257b76a94d988f747a87850b7e114?d=identicon" alt="ghp-dev " width="32" title="ghp-dev "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>1 minute to read</pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="images/ea/Manual/exportPPT.png" alt="exportPPT">
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Blog-Post: <a href="https://rdmueller.github.io/PPT-as-asciidoc-editor/">Do more with Slides</a>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_source_13">3.19.1. Source</h4>
<div class="listingblock">
<div class="title">exportPPT.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">task exportPPT(
        <span class="key">dependsOn</span>: [streamingExecute],
        <span class="key">description</span>: <span class="string"><span class="delimiter">'</span><span class="content">exports all slides and some texts from PPT files</span><span class="delimiter">'</span></span>,
        <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">docToolchain</span><span class="delimiter">'</span></span>
) {
    doLast {
        <span class="comment">//make sure path for notes exists</span>
        <span class="comment">//and remove old notes</span>
        <span class="keyword">new</span> <span class="predefined-type">File</span>(projectDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/ppt</span><span class="delimiter">'</span></span>).deleteDir()
        <span class="comment">//also remove old diagrams</span>
        <span class="keyword">new</span> <span class="predefined-type">File</span>(projectDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/images/ppt</span><span class="delimiter">'</span></span>).deleteDir()
        <span class="comment">//create a readme to clarify things</span>
        <span class="keyword">def</span> readme = <span class="string"><span class="delimiter">&quot;&quot;&quot;</span><span class="content">This folder contains exported slides or notes from .ppt presentations.

Please note that these are generated files but reside in the `src`-folder in order to be versioned.

This is to make sure that they can be used from environments other than windows.

# Warning!

**The contents of this folder        will be overwritten with each re-export!**

use `gradle exportPPT` to re-export files
</span><span class="delimiter">&quot;&quot;&quot;</span></span>
        <span class="keyword">new</span> <span class="predefined-type">File</span>(projectDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/images/ppt/.</span><span class="delimiter">'</span></span>).mkdirs()
        <span class="keyword">new</span> <span class="predefined-type">File</span>(projectDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/images/ppt/readme.ad</span><span class="delimiter">'</span></span>).write(readme)
        <span class="keyword">new</span> <span class="predefined-type">File</span>(projectDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/ppt/.</span><span class="delimiter">'</span></span>).mkdirs()
        <span class="keyword">new</span> <span class="predefined-type">File</span>(projectDir, <span class="string"><span class="delimiter">'</span><span class="content">src/docs/ppt/readme.ad</span><span class="delimiter">'</span></span>).write(readme)
        <span class="comment">//execute through cscript in order to make sure that we get WScript.echo right</span>
        <span class="string"><span class="delimiter">&quot;</span><span class="content">%SystemRoot%</span><span class="char">\\</span><span class="content">System32</span><span class="char">\\</span><span class="content">cscript.exe //nologo </span><span class="inline"><span class="inline-delimiter">${</span>projectDir<span class="inline-delimiter">}</span></span><span class="content">/scripts/exportPPT.vbs</span><span class="delimiter">&quot;</span></span>.executeCmd()
    }
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">exportPPT.vbs</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="vbs">    Const   ForAppending = 8
    Const   ppPlaceholderBody = 2

    ' Helper
    ' http://windowsitpro.com/windows/jsi-tip-10441-how-can-vbscript-create-multiple-folders-path-mkdir-command
    Function MakeDir (strPath)
      Dim strParentPath, objFSO
      Set objFSO = CreateObject(&quot;Scripting.FileSystemObject&quot;)
      On Error Resume Next
      strParentPath = objFSO.GetParentFolderName(strPath)

      If Not objFSO.FolderExists(strParentPath) Then MakeDir strParentPath
      If Not objFSO.FolderExists(strPath) Then objFSO.CreateFolder strPath
      On Error Goto 0
      MakeDir = objFSO.FolderExists(strPath)

    End Function

        Function SearchPresentations(path)

                  For Each folder In path.SubFolders
                    SearchPresentations folder
                  Next

                  For Each file In path.Files
                                If (Left(fso.GetExtensionName (file.Path), 3) = &quot;ppt&quot;) OR (Left(fso.GetExtensionName (file.Path), 3) = &quot;pps&quot;)  Then
                                        WScript.echo &quot;found &quot;&amp;file.path
                                        ExportSlides(file.Path)
                                End If
                  Next

    End Function

    Sub ExportSlides(sFile)
        Set objRegEx = CreateObject(&quot;VBScript.RegExp&quot;)
        objRegEx.Global = True
        objRegEx.IgnoreCase = True
        objRegEx.MultiLine = True
        ' &quot;.&quot; doesn't work for multiline in vbs, &quot;[\s,\S]&quot; does...
        objRegEx.Pattern = &quot;[\s,\S]*{adoc}&quot;
        ' http://www.pptfaq.com/FAQ00481_Export_the_notes_text_of_a_presentation.htm
        strFileName = fso.GetFIle(sFile).Name
        Set oPPT = CreateObject(&quot;PowerPoint.Application&quot;)
        Set oPres = oPPT.Presentations.Open(sFile, True, False, False) ' Read Only, No Title, No Window
        Set oSlides = oPres.Slides
        strNotesText = &quot;&quot;
        strImagePath = &quot;/src/docs/images/ppt/&quot; &amp; strFileName &amp; &quot;/&quot;
        MakeDir(&quot;.&quot; &amp; strImagePath)
        strNotesPath = &quot;/src/docs/ppt/&quot;
        MakeDir(&quot;.&quot; &amp; strNotesPath)
        For Each oSl In oSlides
           strSlideName = oSl.Name
           ' WScript.echo fso.GetAbsolutePathName(&quot;.&quot;) &amp; strImagePath &amp; strSlideName &amp; &quot;.jpg&quot;
           oSl.Export fso.GetAbsolutePathName(&quot;.&quot;) &amp; strImagePath &amp; strSlideName &amp; &quot;.jpg&quot;, &quot;.jpg&quot;
            For Each oSh In oSl.NotesPage.Shapes
                If oSh.PlaceholderFormat.Type = ppPlaceholderBody  Then
                    If oSh.HasTextFrame Then
                        If oSh.TextFrame.HasText Then
                            strCurrentNotes = oSh.TextFrame.TextRange.Text
                            strCurrentNotes = Replace(strCurrentNotes,vbVerticalTab, vbCrLf)
                            strCurrentNotes = Replace(strCurrentNotes,&quot;{slide}&quot;,&quot;image::ppt/&quot;&amp;strFileName&amp;&quot;/&quot;&amp;strSlideName&amp;&quot;.jpg[]&quot;)
                            ' remove speaker notes before marker &quot;{adoc}&quot;
                            strCurrentNotes = objRegEx.Replace(strCurrentNotes,&quot;&quot;)
                            strNotesText = strNotesText  &amp; vbCrLf &amp; strCurrentNotes &amp; vbCrLf &amp; vbCrLf
                        End If
                    End If
                End If
            Next
        Next
        ' WScript.echo fso.GetAbsolutePathName(&quot;.&quot;) &amp; strNotesPath&amp;&quot;&quot;&amp;strFileName&amp;&quot;.ad&quot;
        ' http://stackoverflow.com/questions/2524703/save-text-file-utf-8-encoded-with-vba

        Set fsT = CreateObject(&quot;ADODB.Stream&quot;)
        fsT.Type = 2 'Specify stream type - we want To save text/string data.
        fsT.Charset = &quot;utf-8&quot; 'Specify charset For the source text data.
        fsT.Open 'Open the stream And write binary data To the object
        fsT.WriteText &quot;ifndef::imagesdir[:imagesdir: ../../images]&quot;&amp;vbCrLf&amp;CStr(strNotesText)
        fsT.SaveToFile fso.GetAbsolutePathName(&quot;.&quot;) &amp; strNotesPath&amp;&quot;&quot;&amp;strFileName&amp;&quot;.ad&quot;, 2 'Save binary data To disk
        oPres.Close()
        oPPT.Quit()
    End Sub

  set fso = CreateObject(&quot;Scripting.fileSystemObject&quot;)
  WScript.echo &quot;Slide extractor&quot;
  WScript.echo &quot;looking for .ppt files in &quot; &amp; fso.GetAbsolutePathName(&quot;.&quot;) &amp; &quot;/src&quot;
  SearchPresentations fso.GetFolder(&quot;./src&quot;)
  WScript.echo &quot;finished exporting slides&quot;</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect2">
<h3 id="_exportexcel">3.20. exportExcel</h3>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_exportExcel.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_exportExcel.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/34ef1437fd31ff96c31dff58125e93fe?d=identicon" alt="Dr. Stefan Pfeiffer " width="32" title="Dr. Stefan Pfeiffer "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/eedf6f2d4b31cc4e49a7aa4fc55f51d3?d=identicon" alt="Ralf D. Müller " width="32" title="Ralf D. Müller "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>1 minute to read</pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="images/ea/Manual/exportExcel.png" alt="exportExcel">
</div>
</div>
<div class="paragraph">
<p>Sometimes you have tabular data to be included in your documentation.
Then it is likely that the data is available as Excel sheet or you would like to use MS Excel to create and edit it.</p>
</div>
<div class="paragraph">
<p>Either way, this task lets you export your Excel sheet and include it directly in your docs.</p>
</div>
<div class="paragraph">
<p>The task searches for <code>.xlsx</code> files and exports each contained worksheet as <code>.csv</code> and as <code>.adoc</code>.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Formulas contained in your worksheet are evaluated and exported statically.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The generated files are written to <code>src/excel/[filename]/[worksheet].(adoc|cvs)</code> .
The <code>src</code> folder is chosen over the <code>build</code> folder to get a better history for all changes on the worksheets.</p>
</div>
<div class="paragraph">
<p>The files can be included either as AsciiDoc</p>
</div>
<div class="listingblock">
<div class="content">
<pre>include::excel/Sample.xlsx/Numerisch.adoc[]</pre>
</div>
</div>
<div class="paragraph">
<p>or as a CSV file</p>
</div>
<div class="listingblock">
<div class="content">
<pre>[options="header",format="csv"]
|===
include::excel/Sample.xlsx/Numerisch.csv[]
|===</pre>
</div>
</div>
<div class="paragraph">
<p>The AsciiDoc version gives you a bit more control:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>horizontal and vertical alignment is preserved</p>
</li>
<li>
<p>col- and row-spans are preserved</p>
</li>
<li>
<p>line breaks are preserved</p>
</li>
<li>
<p>column width relative to other columns is preserved</p>
</li>
<li>
<p>background colors are preserved.</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="_source_14">3.20.1. Source</h4>
<div class="listingblock">
<div class="title">build.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">task exportExcel(
        <span class="key">description</span>: <span class="string"><span class="delimiter">'</span><span class="content">exports all excelsheets to csv and AsciiDoc</span><span class="delimiter">'</span></span>,
        <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">docToolchain</span><span class="delimiter">'</span></span>
) {
    doFirst {
        <span class="predefined-type">File</span> sourceDir = file(srcDir)

        <span class="keyword">def</span> tree = fileTree(srcDir).include(<span class="string"><span class="delimiter">'</span><span class="content">**/*.xlsx</span><span class="delimiter">'</span></span>).exclude(<span class="string"><span class="delimiter">'</span><span class="content">**/~*</span><span class="delimiter">'</span></span>)

        <span class="keyword">def</span> exportFileDir = <span class="keyword">new</span> <span class="predefined-type">File</span>(sourceDir, <span class="string"><span class="delimiter">'</span><span class="content">excel</span><span class="delimiter">'</span></span>)

        <span class="comment">//make sure path for notes exists</span>
        exportFileDir.deleteDir()
        <span class="comment">//create a readme to clarify things</span>
        <span class="keyword">def</span> readme = <span class="string"><span class="delimiter">&quot;&quot;&quot;</span><span class="content">This folder contains exported workbooks from Excel.

Please note that these are generated files but reside in the `src`-folder in order to be versioned.

This is to make sure that they can be used from environments other than windows.

# Warning!

**The contents of this folder will be overwritten with each re-export!**

use `gradle exportExcel` to re-export files
</span><span class="delimiter">&quot;&quot;&quot;</span></span>
        exportFileDir.mkdirs()
        <span class="keyword">new</span> <span class="predefined-type">File</span>(exportFileDir, <span class="string"><span class="delimiter">'</span><span class="content">/readme.ad</span><span class="delimiter">'</span></span>).write(readme)
    }
    doLast {
        <span class="predefined-type">File</span> sourceDir = file(srcDir)
        <span class="keyword">def</span> exportFileDir = <span class="keyword">new</span> <span class="predefined-type">File</span>(sourceDir, <span class="string"><span class="delimiter">'</span><span class="content">excel</span><span class="delimiter">'</span></span>)
        <span class="keyword">def</span> tree = fileTree(srcDir).include(<span class="string"><span class="delimiter">'</span><span class="content">**/*.xlsx</span><span class="delimiter">'</span></span>).exclude(<span class="string"><span class="delimiter">'</span><span class="content">**/~*</span><span class="delimiter">'</span></span>)

        <span class="keyword">def</span> nl = <span class="predefined-type">System</span>.getProperty(<span class="string"><span class="delimiter">&quot;</span><span class="content">line.separator</span><span class="delimiter">&quot;</span></span>)

        <span class="keyword">def</span> <span class="keyword">export</span> = { sheet, evaluator, targetFileName -&gt;
            <span class="keyword">def</span> targetFileCSV = <span class="keyword">new</span> <span class="predefined-type">File</span>(targetFileName + <span class="string"><span class="delimiter">'</span><span class="content">.csv</span><span class="delimiter">'</span></span>)
            <span class="keyword">def</span> targetFileAD = <span class="keyword">new</span> <span class="predefined-type">File</span>(targetFileName + <span class="string"><span class="delimiter">'</span><span class="content">.adoc</span><span class="delimiter">'</span></span>)
            <span class="keyword">def</span> df = <span class="keyword">new</span> org.apache.poi.ss.usermodel.DataFormatter();
            <span class="keyword">def</span> regions = <span class="type">[]</span>
            sheet.numMergedRegions.times {
                regions &lt;&lt; sheet.getMergedRegion(<span class="local-variable">it</span>)
            }
            logger.debug <span class="string"><span class="delimiter">&quot;</span><span class="content">sheet contains </span><span class="inline"><span class="inline-delimiter">${</span>regions.size()<span class="inline-delimiter">}</span></span><span class="content"> regions</span><span class="delimiter">&quot;</span></span>
            <span class="keyword">def</span> color = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
            <span class="keyword">def</span> resetColor = <span class="predefined-constant">false</span>
            <span class="keyword">def</span> numCols = <span class="integer">0</span>
            <span class="keyword">def</span> headerCreated = <span class="predefined-constant">false</span>
            <span class="keyword">def</span> emptyRows = <span class="integer">0</span>
            <span class="keyword">for</span> (<span class="type">int</span> rowNum=<span class="integer">0</span>; rowNum&lt;=sheet.lastRowNum; rowNum++) {
                <span class="keyword">def</span> row = sheet.getRow(rowNum)
                <span class="keyword">if</span> (row &amp;&amp; !headerCreated) {
                    headerCreated = <span class="predefined-constant">true</span>
                    <span class="comment">// create AsciiDoc table header</span>
                    <span class="keyword">def</span> width = <span class="type">[]</span>
                    numCols = row.lastCellNum
                    numCols.times { columnIndex -&gt;
                        width &lt;&lt; sheet.getColumnWidth((<span class="type">int</span>) columnIndex)
                    }
                    <span class="comment">//lets make those numbers nicer:</span>
                    width = width.collect { <span class="predefined-type">Math</span>.round(<span class="integer">100</span> * <span class="local-variable">it</span> / width.sum()) }
                    targetFileAD.append(<span class="string"><span class="delimiter">'</span><span class="content">[options=&quot;header&quot;,cols=&quot;</span><span class="delimiter">'</span></span> + width.join(<span class="string"><span class="delimiter">'</span><span class="content">,</span><span class="delimiter">'</span></span>) + <span class="string"><span class="delimiter">'</span><span class="content">&quot;]</span><span class="delimiter">'</span></span> + nl)
                    targetFileAD.append(<span class="string"><span class="delimiter">'</span><span class="content">|===</span><span class="delimiter">'</span></span> + nl)
                }
                <span class="keyword">def</span> data = <span class="type">[]</span>
                <span class="keyword">def</span> style = <span class="type">[]</span>
                <span class="keyword">def</span> colors = <span class="type">[]</span>
                <span class="comment">// For each row, iterate through each columns</span>
                <span class="keyword">if</span> (row &amp;&amp; (row?.lastCellNum!=-<span class="integer">1</span>)) {
                    numCols.times { columnIndex -&gt;
                        <span class="keyword">def</span> cell = row.getCell(columnIndex)
                        <span class="keyword">if</span> (cell) {
                            <span class="keyword">def</span> cellValue = df.formatCellValue(cell, evaluator)
                            <span class="keyword">if</span> (cellValue.startsWith(<span class="string"><span class="delimiter">'</span><span class="content">*</span><span class="delimiter">'</span></span>) &amp;&amp; cellValue.endsWith(<span class="string"><span class="delimiter">'</span><span class="content">\u20AC</span><span class="delimiter">'</span></span>)) {
                                <span class="comment">// Remove special characters at currency</span>
                                cellValue = cellValue.substring(<span class="integer">1</span>).trim();
                            }
                            <span class="keyword">def</span> cellStyle = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
                            <span class="keyword">def</span> region = regions.find { <span class="local-variable">it</span>.isInRange(cell.rowIndex, cell.columnIndex) }
                            <span class="keyword">def</span> skipCell = <span class="predefined-constant">false</span>
                            <span class="keyword">if</span> (region) {
                                <span class="comment">//check if we are in the upper left corner of the region</span>
                                <span class="keyword">if</span> (region.firstRow == cell.rowIndex &amp;&amp; region.firstColumn == cell.columnIndex) {
                                    <span class="keyword">def</span> colspan = <span class="integer">1</span> + region.lastRow - region.firstRow
                                    <span class="keyword">def</span> rowspan = <span class="integer">1</span> + region.lastColumn - region.firstColumn
                                    <span class="keyword">if</span> (rowspan &gt; <span class="integer">1</span>) {
                                        cellStyle += <span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">${</span>rowspan<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>
                                    }
                                    <span class="keyword">if</span> (colspan &gt; <span class="integer">1</span>) {
                                        cellStyle += <span class="string"><span class="delimiter">&quot;</span><span class="content">.</span><span class="inline"><span class="inline-delimiter">${</span>colspan<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>
                                    }
                                    cellStyle += <span class="string"><span class="delimiter">&quot;</span><span class="content">+</span><span class="delimiter">&quot;</span></span>
                                } <span class="keyword">else</span> {
                                    skipCell = <span class="predefined-constant">true</span>
                                }
                            }
                            <span class="keyword">if</span> (!skipCell) {
                                <span class="keyword">switch</span> (cell.cellStyle.alignmentEnum.toString()) {
                                    <span class="keyword">case</span> <span class="string"><span class="delimiter">'</span><span class="content">RIGHT</span><span class="delimiter">'</span></span>:
                                        cellStyle += <span class="string"><span class="delimiter">'</span><span class="content">&gt;</span><span class="delimiter">'</span></span>
                                        <span class="keyword">break</span>
                                    <span class="keyword">case</span> <span class="string"><span class="delimiter">'</span><span class="content">CENTER</span><span class="delimiter">'</span></span>:
                                        cellStyle += <span class="string"><span class="delimiter">'</span><span class="content">^</span><span class="delimiter">'</span></span>
                                        <span class="keyword">break</span>
                                }
                                <span class="keyword">switch</span> (cell.cellStyle.verticalAlignmentEnum.toString()) {
                                    <span class="keyword">case</span> <span class="string"><span class="delimiter">'</span><span class="content">BOTTOM</span><span class="delimiter">'</span></span>:
                                        cellStyle += <span class="string"><span class="delimiter">'</span><span class="content">.&gt;</span><span class="delimiter">'</span></span>
                                        <span class="keyword">break</span>
                                    <span class="keyword">case</span> <span class="string"><span class="delimiter">'</span><span class="content">CENTER</span><span class="delimiter">'</span></span>:
                                        cellStyle += <span class="string"><span class="delimiter">'</span><span class="content">.^</span><span class="delimiter">'</span></span>
                                        <span class="keyword">break</span>
                                }
                                color = cell.cellStyle.fillForegroundXSSFColor?.RGB?.encodeHex()
                                color = color != <span class="predefined-constant">null</span> ? nl + <span class="string"><span class="delimiter">&quot;</span><span class="content">{set:cellbgcolor:#</span><span class="inline"><span class="inline-delimiter">${</span>color<span class="inline-delimiter">}</span></span><span class="content">}</span><span class="delimiter">&quot;</span></span> : <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>
                                data &lt;&lt; cellValue
                                <span class="keyword">if</span> (color == <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span> &amp;&amp; resetColor) {
                                    colors &lt;&lt; nl + <span class="string"><span class="delimiter">&quot;</span><span class="content">{set:cellbgcolor!}</span><span class="delimiter">&quot;</span></span>
                                    resetColor = <span class="predefined-constant">false</span>
                                } <span class="keyword">else</span> {
                                    colors &lt;&lt; color
                                }
                                <span class="keyword">if</span> (color != <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>) {
                                    resetColor = <span class="predefined-constant">true</span>
                                }
                                style &lt;&lt; cellStyle
                            } <span class="keyword">else</span> {
                                data &lt;&lt; <span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span>
                                colors &lt;&lt; <span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span>
                                style &lt;&lt; <span class="string"><span class="delimiter">&quot;</span><span class="content">skip</span><span class="delimiter">&quot;</span></span>
                            }
                        } <span class="keyword">else</span> {
                            data &lt;&lt; <span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span>
                            colors &lt;&lt; <span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span>
                            style &lt;&lt; <span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span>
                        }

                    }
                    emptyRows = <span class="integer">0</span>
                } <span class="keyword">else</span> {
                    <span class="keyword">if</span> (emptyRows&lt;<span class="integer">3</span>) {
                        <span class="comment">//insert empty row</span>
                        numCols.times {
                            data &lt;&lt; <span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span>
                            colors &lt;&lt; <span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span>
                            style &lt;&lt; <span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span>
                        }
                        emptyRows++
                    } <span class="keyword">else</span> {
                        <span class="keyword">break</span>
                    }
                }

                targetFileCSV.append(data
                        .collect {
                    <span class="string"><span class="delimiter">&quot;</span><span class="char">\&quot;</span><span class="inline"><span class="inline-delimiter">${</span><span class="local-variable">it</span>.replaceAll(<span class="string"><span class="delimiter">'</span><span class="content">&quot;</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">&quot;&quot;</span><span class="delimiter">'</span></span>)<span class="inline-delimiter">}</span></span><span class="char">\&quot;</span><span class="delimiter">&quot;</span></span>
                }
                .join(<span class="string"><span class="delimiter">'</span><span class="content">,</span><span class="delimiter">'</span></span>) + nl, <span class="string"><span class="delimiter">'</span><span class="content">UTF-8</span><span class="delimiter">'</span></span>)
                targetFileAD.append(data
                        .withIndex()
                        .collect { value, index -&gt;
                    <span class="keyword">if</span> (style[index] == <span class="string"><span class="delimiter">&quot;</span><span class="content">skip</span><span class="delimiter">&quot;</span></span>) {
                        <span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span>
                    } <span class="keyword">else</span> {
                        style[index] + <span class="string"><span class="delimiter">&quot;</span><span class="content">| </span><span class="inline"><span class="inline-delimiter">${</span>value.replaceAll(<span class="string"><span class="delimiter">'</span><span class="content">[|]</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">{vbar}</span><span class="delimiter">'</span></span>).replaceAll(<span class="string"><span class="delimiter">&quot;</span><span class="char">\n</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">'</span><span class="content"> +$0</span><span class="delimiter">'</span></span>) + colors[index]<span class="inline-delimiter">}</span></span><span class="delimiter">&quot;</span></span>
                    }
                }
                .join(nl) + nl * <span class="integer">2</span>, <span class="string"><span class="delimiter">'</span><span class="content">UTF-8</span><span class="delimiter">'</span></span>)
            }
            targetFileAD.append(<span class="string"><span class="delimiter">'</span><span class="content">|===</span><span class="delimiter">'</span></span> + nl)
        }


        tree.each { <span class="predefined-type">File</span> excel -&gt;
            println excel
            <span class="keyword">def</span> excelDir = <span class="keyword">new</span> <span class="predefined-type">File</span>(exportFileDir, excel.getName())
            excelDir.mkdirs()
            <span class="predefined-type">InputStream</span> inp
            inp = <span class="keyword">new</span> <span class="predefined-type">FileInputStream</span>(excel)
            <span class="keyword">def</span> wb = org.apache.poi.ss.usermodel.WorkbookFactory.create(inp);
            <span class="keyword">def</span> evaluator = wb.getCreationHelper().createFormulaEvaluator();
            <span class="keyword">for</span> (<span class="type">int</span> wbi = <span class="integer">0</span>; wbi &lt; wb.getNumberOfSheets(); wbi++) {
                <span class="keyword">def</span> sheetName = wb.getSheetAt(wbi).getSheetName()
                println sheetName
                <span class="keyword">def</span> targetFile = <span class="keyword">new</span> <span class="predefined-type">File</span>(excelDir, sheetName)
                <span class="keyword">export</span>(wb.getSheetAt(wbi), evaluator, targetFile.getAbsolutePath())
            }
            inp.close();
        }
    }
}</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect2">
<h3 id="_exportmarkdown">3.21. exportMarkdown</h3>
<div class="paragraph">
<p>The <em>exportMarkdown</em> task can be used to include <a href="https://markdown.de">markdown</a> files into the documentation.</p>
</div>
<div class="paragraph">
<p>The task is scanning the directory <code>/src/docs</code> for markdown files (<code>*.md</code>) and converts them into AsciiDoc files. The converted files can then be referenced from within the <code>/build</code>-folder.</p>
</div>
<div class="sect3">
<h4 id="_source_15">3.21.1. Source</h4>
<div class="listingblock">
<div class="title">exportMarkdown.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">task exportMarkdown(
        <span class="key">description</span>: <span class="string"><span class="delimiter">'</span><span class="content">exports all markdown files to AsciiDoc</span><span class="delimiter">'</span></span>,
        <span class="key">group</span>: <span class="string"><span class="delimiter">'</span><span class="content">docToolchain</span><span class="delimiter">'</span></span>,
        <span class="key">type</span>: Copy
) {
    from srcDir

    include(<span class="string"><span class="delimiter">&quot;</span><span class="content">**/*.md</span><span class="delimiter">&quot;</span></span>) <span class="comment">//include only markdown files</span>
    includeEmptyDirs = <span class="predefined-constant">false</span>
    rename(<span class="regexp"><span class="delimiter">/</span><span class="content">(.+).md</span><span class="delimiter">/</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">$1.adoc</span><span class="delimiter">'</span></span>) <span class="comment">//rename all files from *.md to *.adoc</span>
    filter(Markdown2AdocFilter) <span class="comment">// convert the content of the files</span>

    into targetDir
}

<span class="type">class</span> <span class="class">Markdown2AdocFilter</span> <span class="directive">extends</span> <span class="predefined-type">FilterReader</span> {
    Markdown2AdocFilter(<span class="predefined-type">Reader</span> input) {
        <span class="local-variable">super</span>(<span class="keyword">new</span> <span class="predefined-type">StringReader</span>(nl.jworks.markdown_to_asciidoc.Converter.convertMarkdownToAsciiDoc(input.text)))
    }
}</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect2">
<h3 id="_htmlsanitycheck">3.22. htmlSanityCheck</h3>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_htmlSanityCheck.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_htmlSanityCheck.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5f528a3f6814d28b583f31842e3e8d9e?d=identicon" alt="Alexander Schwartz " width="32" title="Alexander Schwartz "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/34ef1437fd31ff96c31dff58125e93fe?d=identicon" alt="Stefan Pfeiffer " width="32" title="Stefan Pfeiffer "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>1 minute to read</pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="images/ea/Manual/htmlSanityCheck.png" alt="htmlSanityCheck">
</div>
</div>
<div class="paragraph">
<p>This task invokes the <a href="https://github.com/aim42/htmlSanityCheck">htmlSanityCheck</a> gradle plugin.
It is a standalone (batch- and command-line) html sanity checker - it detects missing images, dead links, and duplicate bookmarks.</p>
</div>
<div class="paragraph">
<p>In docToolchain, this task is used to ensure that the generated HTML contains no missing links or other problems.</p>
</div>
<div class="paragraph">
<p>This task is the last default task and creates a report in <code>build/report/htmlchecks/index.html</code></p>
</div>
<div class="imageblock text-center">
<div class="content">
<img src="images/manual/htmlSanityCheck.png" alt="htmlSanityCheck">
</div>
<div class="title">Figure 5. sample report</div>
</div>
<div class="paragraph">
<p>Further information can be found on GitHub: <a href="https://github.com/aim42/htmlSanityCheck" class="bare">https://github.com/aim42/htmlSanityCheck</a></p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Blog-Post: <a href="https://rdmueller.github.io/htmlSanityCheck/">Automated Quality-Checks</a>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_source_16">3.22.1. Source</h4>
<div class="listingblock">
<div class="title">htmlSanityCheck.gradle</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">htmlSanityCheck {
    sourceDir = <span class="keyword">new</span> <span class="predefined-type">File</span>(<span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">$</span>targetDir</span><span class="content">/html5</span><span class="delimiter">&quot;</span></span>)

    <span class="comment">// files to check - in Set-notation</span>
    <span class="comment">//sourceDocuments = [ &quot;one-file.html&quot;, &quot;another-file.html&quot;, &quot;index.html&quot;]</span>

    <span class="comment">// where to put results of sanityChecks...</span>
    checkingResultsDir = <span class="keyword">new</span> <span class="predefined-type">File</span>( checkingResultsPath )
    <span class="comment">//checkExternalLinks = false</span>
}</code></pre>
</div>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect2">
<h3 id="_dependencyupdates">3.23. dependencyUpdates</h3>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/03_task_dependencyUpdates.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/03_task_dependencyUpdates.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5f528a3f6814d28b583f31842e3e8d9e?d=identicon" alt="Alexander Schwartz " width="32" title="Alexander Schwartz "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/34ef1437fd31ff96c31dff58125e93fe?d=identicon" alt="Dr. Stefan Pfeiffer " width="32" title="Dr. Stefan Pfeiffer "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/2e3af23bd872d2b971788a4519e3bf46?d=identicon" alt="jakubjab " width="32" title="jakubjab "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>1 minute to read</pre>
</div>
</div>
<div class="paragraph">
<p>This task uses the <a href="https://github.com/ben-manes/gradle-versions-plugin">Gradle versions plugin</a> created by Ben Manes to check for outdated build dependencies.
It is quite helpful to keep all dependencies up-to-date.</p>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
if you discover newer version, this does not mean that they will play nicely together.
If you want to make sure that everything works, rely on the versions which the docToolchain contributors have selected.
</td>
</tr>
</table>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Blog-Post: <a href="https://rdmueller.github.io/dependency-update/">Handle Dependency Updates the easy Way</a>
</td>
</tr>
</table>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_development">4. Development</h2>
<div class="sectionbody">
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/08_development.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/08_development.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Müller " width="32" title="Ralf D. Müller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5f528a3f6814d28b583f31842e3e8d9e?d=identicon" alt="Alexander Schwartz " width="32" title="Alexander Schwartz "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>1 minute to read</pre>
</div>
</div>
<div class="paragraph">
<p>INFO: this chapter is still work in progress</p>
</div>
<div class="sect2">
<h3 id="_how_to_run_tests">4.1. How to run Tests</h3>
<div class="sect3">
<h4 id="_prerequisites">4.1.1. Prerequisites</h4>
<div class="ulist">
<ul>
<li>
<p>make sure that Git and Graphviz are installed</p>
</li>
<li>
<p>make sure that your Gradle setup is able to work with proxies</p>
</li>
<li>
<p>use Java 8</p>
</li>
</ul>
</div>
</div>
<div class="sect3">
<h4 id="_prepare_the_project">4.1.2. Prepare the Project</h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">git clone git@github.com:docToolchain/docToolchain.git
cd docToolchain/
git checkout V1.0.0         <i class="conum" data-value="1"></i><b>(1)</b>
git submodule update -i</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>the version to test.
Not needed if you work on the HEAD revision on Master</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_execute_tests">4.1.3. Execute Tests</h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">rm -r build &amp;&amp; ./gradlew test --info</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>rm</code> command ensures that you really have a clean test running.
Otherwise you might get false positives because Gradle will skip steps ('Up-to-date') because artifacts of an older test run still exist.</p>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_faq_frequently_asked_questions">5. FAQ: Frequently asked Questions</h2>
<div class="sectionbody">
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/06_Frequently_asked_Questions.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/06_Frequently_asked_Questions.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5f528a3f6814d28b583f31842e3e8d9e?d=identicon" alt="Alexander Schwartz " width="32" title="Alexander Schwartz "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/34ef1437fd31ff96c31dff58125e93fe?d=identicon" alt="Dr. Stefan Pfeiffer " width="32" title="Dr. Stefan Pfeiffer "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/69124393772349d32fb9fe975545757d?d=identicon" alt="Lars Francke " width="32" title="Lars Francke "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/fdbd46a8418be834d8ab743ca4b52455?d=identicon" alt="funkr " width="32" title="funkr "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/2e3af23bd872d2b971788a4519e3bf46?d=identicon" alt="jakubjab " width="32" title="jakubjab "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>6 minutes to read</pre>
</div>
</div>
<div class="paragraph">
<p>This section tries to answer the most common and frequently asked questions about how to work with docToolchain.
It will also contain questions relevant to the tools used to build docToolchain, but the main focus is docToolchain itself.</p>
</div>
<div class="paragraph">
<p>If you are stuck, make sure that you also check other sources like <a href="https://stackoverflow.com/questions/tagged/asciidoctor">Stack Overflow</a>.</p>
</div>
<div class="paragraph">
<p>There is also a great FAQ for all your arc42 questions: <a href="https://faq.arc42.org/home/" class="bare">https://faq.arc42.org/home/</a></p>
</div>
<div class="paragraph">
<p>If you have a question or problem for which you can&#8217;t find a solution, you can</p>
</div>
<div class="ulist">
<ul>
<li>
<p>for this repo, add your question and create a pull request</p>
</li>
<li>
<p>raise the issue through the <a href="https://github.com/docToolchain/docToolchain/issues">GitHub issue tracker</a></p>
</li>
<li>
<p>ask your question on <a href="https://stackoverflow.com/questions/tagged/doctoolchain?sort=active&amp;pageSize=50">Stack Overflow</a></p>
</li>
<li>
<p>discuss the problem on <a href="https://t.co/lnqrUc1YMM">Slack</a></p>
</li>
</ul>
</div>
<div class="sect2">
<h3 id="_images">5.1. Images</h3>
<div class="ulist">
<ul>
<li>
<p><a href="https://asciidoctor.org/docs/user-manual/#images">Asciidoctor User Manual on images</a></p>
</li>
<li>
<p><a href="https://asciidoctor.org/docs/asciidoc-syntax-quick-reference/#images">Asciidoctor Quick Reference on images</a></p>
</li>
<li>
<p><a href="https://asciidoctor.org/docs/asciidoc-writers-guide/#images">AsciiDoctor Writer Guide on images</a></p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="_q_why_are_images_not_shown_in_the_preview_of_my_editor">5.1.1. Q: Why are images not shown in the preview of my editor?</h4>
<div class="paragraph">
<p>A: this is most likely because your editor doesn&#8217;t know where they are stored.
If you follow the default settings, you probably store your images in a subfolder <code>images</code>.
The build script knows about it, because the attribute <code>imagesdir</code> has been set to <code>./images</code>, but your editor doesn&#8217;t care about the build script - it only checks the currently opened AsciiDoc file.</p>
</div>
<div class="paragraph">
<p>The solution is to add a line to each file which checks if the <code>imagesdir</code> is set and if not, sets it to a valid value:</p>
</div>
<div class="paragraph">
<p><code>ifndef::imagesdir[:imagesdir: ../images]</code></p>
</div>
</div>
<div class="sect3">
<h4 id="_q_which_image_format_should_i_use">5.1.2. Q: Which image format should I use?</h4>
<div class="paragraph">
<p>A:
AsciiDoc and AsciiDoctor support several formats like GIF, PNG, JPG and SVG.
However, if you want to use most features, some formats are better to use than others:</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">GIF</dt>
<dd>
<p>is not supported by the PDF renderer.
Use JPG or PNG instead.</p>
</dd>
<dt class="hdlist1">JPG</dt>
<dd>
<p>is great for photos but not for diagrams (you might get compression artifacts).
So, if you want to use photos from your flipcharts - JPG might work for you.</p>
</dd>
<dt class="hdlist1">SVG</dt>
<dd>
<p>great for high resolution diagrams, but not good supported by DOCX as output format.
OpenOffice Writer might display the image a bit stretched, MS Word didn&#8217;t display it at all in some experiments.</p>
</dd>
<dt class="hdlist1">PNG</dt>
<dd>
<p>this is the preferred format for images used with docToolchain.
All output formats support it and if diagrams are rendered with a resolution high enough to display all details, it will also be scaled well with all output formats.</p>
</dd>
</dl>
</div>
</div>
<div class="sect3">
<h4 id="_q_why_are_my_images_rotated_in_the_output">5.1.3. Q: Why are my images rotated in the output?</h4>
<div class="paragraph">
<p>A:
This most likely happens when you&#8217;ve taken photos with a mobile device and include them in you docs.
A mobile device does not rotate the image itself, it only stores the orientation of the device in the meta data of the photo.
Your operating system will show you the image as expected, but the rendered AsciiDoc will not.
This can be „fixed“ with Imagemagick, by using <code>convert -auto-orient</code> or <code>mogrify -auto-orient</code> (thanx to <a href="https://twitter.com/RalfDMueller/status/920888868345384960">@rotnroll666</a> for this tip).
You can also try to just open the image in your favourite editor and re-save it.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_exportvisio_2">5.2. exportVisio</h3>
<div class="sect3">
<h4 id="_q_i_get_an_error_message_saying_that_a_library_is_not_registered_when_i_try_to_run_the_exportvisio_task">5.2.1. Q: I get an error message saying that a library is not registered when I try to run the <code>exportVisio</code>-task.</h4>
<div class="listingblock">
<div class="content">
<pre>Ausnahme beim Festlegen von "Visible": "Das COM-Objekt des Typs "Microsoft.Office.Interop.Visio.ApplicationClass" kann nicht in den Schnittstellentyp
"Microsoft.Office.Interop.Visio.IVApplication" umgewandelt werden. Dieser Vorgang konnte nicht durchgeführt werden, da der QueryInterface-Aufruf an die
COM-Komponente für die Schnittstelle mit der IID "{000D0700-0000-0000-C000-000000000046}" aufgrund des folgenden Fehlers nicht durchgeführt werden konnte:
Bibliothek nicht registriert. (Ausnahme von HRESULT: 0x8002801D (TYPE_E_LIBNOTREGISTERED))."
In ...\scripts\VisioPageToPngConverter.ps1:48 Zeichen:1
+ $VisioApp.Visible = $false
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], SetValueInvocationException
    + FullyQualifiedErrorId : ExceptionWhenSetting</pre>
</div>
</div>
<div class="paragraph">
<p>A:
When Visio is installed, it registers itself as a com library.
It seems that this registration can break.
You can fix this by visiting the windows system settings &#8594; install or uninstall a program, select <code>visio</code>, select <code>change</code> and then <code>repair</code>.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_sparx_enterprise_architect">5.3. Sparx Enterprise Architect</h3>
<div class="sect3">
<h4 id="_q_sparx_enterprise_architect_is_a_windows_tool_isnt_it">5.3.1. Q: Sparx Enterprise Architect is a Windows tool, isn&#8217;t it?</h4>
<div class="paragraph">
<p>Yes, it is, but it is written to support CrossOver in order to run on Linux based systems.
Wine, the open source branch of CrossOver, seems to work as well.</p>
</div>
<div class="paragraph">
<p>Take a look at this page to see how to install it on a linux based system: <a href="https://www.sparxsystems.com/support/faq/ea_on_linux.html" class="bare">https://www.sparxsystems.com/support/faq/ea_on_linux.html</a></p>
</div>
<div class="paragraph">
<p>I (Ralf) once gave it a try and even managed to get remote control over EA via VBS and COM up and running (which is the pre-requisite for docToolchain).</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_known_error_messages">5.4. Known error Messages</h3>
<div class="sect3">
<h4 id="_q_i_get_the_error_saying_failed_to_create_md5_hash_for_file_content">5.4.1. Q: I get the error saying 'Failed to create MD5 hash for file content'.</h4>
<div class="listingblock">
<div class="content">
<pre>* What went wrong:
Failed to capture snapshot of input files for task ':generateHTML' property 'sourceDir' during up-to-date check.
&gt; Failed to create MD5 hash for file content.`</pre>
</div>
</div>
<div class="paragraph">
<p>There are two known reasons for this error.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>One of the .adoc files is opened in an editor, so that windows can&#8217;t get the lock for that file.
&#8594; Close all .adoc files.</p>
</li>
<li>
<p>You use the Bash script <code>doctoolchain</code> on a windows system.
&#8594; Use <code>doctoolchain.bat</code> instead.
It works even in a Bash Shell.</p>
</li>
</ol>
</div>
</div>
<div class="sect3">
<h4 id="_q_i_get_the_error_saying_unsupported_major_minor_version_52_0">5.4.2. Q: I get the error saying 'Unsupported major.minor version 52.0'</h4>
<div class="paragraph">
<p>This is a sign that you use an outdated version of Java.
Please upgrade to Java 8 at least.</p>
</div>
</div>
<div class="sect3">
<h4 id="_q_i_get_an_error_message_saying_error_occurred_during_initialization_of_vm">5.4.3. Q: I get an error message saying 'Error occurred during initialization of VM'</h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code>Starting a Gradle Daemon, 1 incompatible Daemon could not be reused, use --status for details

FAILURE: Build failed with an exception.

* What went wrong:
Unable to start the daemon process.
…
Error occurred during initialization of VM
Could not reserve enough space for 2097152KB object heap</code></pre>
</div>
</div>
<div class="paragraph">
<p>Somehow docToolchain can&#8217;t allocate the memory which is configured out of the box.
Try to free up some memory or comment out the line <code>org.gradle.jvmargs=-Xmx2048m</code> in <code>gradle.properties</code></p>
</div>
<hr>
<div style="page-break-after: always;"></div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_further_reading">6. Further Reading</h2>
<div class="sectionbody">
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/04_further_reading.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/04_further_reading.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5f528a3f6814d28b583f31842e3e8d9e?d=identicon" alt="Alexander Schwartz " width="32" title="Alexander Schwartz "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/69124393772349d32fb9fe975545757d?d=identicon" alt="Lars Francke " width="32" title="Lars Francke "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/34ef1437fd31ff96c31dff58125e93fe?d=identicon" alt="Stefan Pfeiffer " width="32" title="Stefan Pfeiffer "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>3 minutes to read</pre>
</div>
</div>
<div class="paragraph">
<p>This chapter lists some additional references to interesting resources.</p>
</div>
<div class="sect2">
<h3 id="_links">6.1. Links</h3>
<div class="ulist">
<ul>
<li>
<p><strong>AsciiDoc</strong></p>
<div class="ulist">
<ul>
<li>
<p><a href="https://asciidoctor.org/docs/user-manual/">Asciidoctor User-Manual</a></p>
</li>
<li>
<p><a href="https://asciidoctor.org/docs/asciidoc-syntax-quick-reference/">AsciiDoc Syntax Quick Reference</a></p>
</li>
</ul>
</div>
</li>
<li>
<p><strong>arc42</strong></p>
<div class="ulist">
<ul>
<li>
<p><a href="https://arc42.org/">arc42</a></p>
</li>
<li>
<p><a href="https://docs.arc42.org/home/">arc42 Tips &amp; Tricks</a></p>
</li>
<li>
<p><a href="https://faq.arc42.org/home/">arc42 FAQ</a></p>
</li>
</ul>
</div>
</li>
<li>
<p><a href="https://rdmueller.github.io/">the docToolchain Blog</a></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_books">6.2. Books</h3>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
links to Amazon are affiliate links
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_english_books">6.2.1. English Books</h4>
<div class="ulist">
<ul>
<li>
<p><a href="https://www.amazon.de/dp/B0784ZJWSR">DOCS-LIKE-CODE</a> by Anne Gentle</p>
</li>
<li>
<p><a href="https://amzn.to/2sn0kyx">Modern technical writing:
An Introduction to Software Documentation</a> by Andrew Etter</p>
</li>
<li>
<p><a href="https://leanpub.com/arc42byexample">arc42 by Example</a> by Gernot Starke, Stefan Zörner, Michael Simons</p>
</li>
<li>
<p><a href="https://leanpub.com/arc42inpractice">Communicating Software Architectures with arc42</a> by Gernot Starke und Peter Hruschka</p>
</li>
<li>
<p><a href="https://leanpub.com/visualising-software-architecture">Visualise, document and explore your software architecture</a> by Simon Brown</p>
</li>
</ul>
</div>
</div>
<div class="sect3">
<h4 id="_german_books">6.2.2. German Books</h4>
<div class="ulist">
<ul>
<li>
<p><a href="https://amzn.to/2smM5Kp">arc42 in Aktion:
Praktische Tipps zur Architekturdokumentation</a> von Gernot Starke und Peter Hruschka</p>
</li>
<li>
<p><a href="https://amzn.to/2ru9Yvr">Softwarearchitekturen dokumentieren und kommunizieren:
Entwürfe, Entscheidungen und Lösungen nachvollziehbar und wirkungsvoll festhalten</a> von Stefan Zörner</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_past_and_upcoming_talks">6.3. Past and upcoming Talks</h3>
<div class="sect3">
<h4 id="_dokumentation_am_riesen_beispiel_arc42_asciidoc_und_co_in_aktion">6.3.1. Dokumentation am (Riesen-)Beispiel – arc42, AsciiDoc und Co. in Aktion</h4>
<div class="paragraph">
<p><a href="https://twitter.com/gernotstarke">Gernot Starke</a> and <a href="https://twitter.com/ralfdmueller">Ralf D.
Müller</a></p>
</div>
<div class="paragraph">
<p>Anhand eines großen Systems zeigen Gernot und Ralf, wie Sie mit ziemlich wenig Aufwand angemessene und vernünftige Dokumentation für unterschiedliche Stakeholder produzieren – sodass Entwicklungsteams dabei auch noch Spaß haben.</p>
</div>
<div class="paragraph">
<p>Unser Rezept:
AsciiDoc mit arc42 mischen, Automatisierung mit Gradle und Maven hinzufügen und mit Diagramm- oder Modellierungstools Ihrer Wahl kombinieren.
Schon sind schicke HTML- und reviewfähige PDF-Dokumente fertig.
Auf Wunsch gibts DOCX und Confluence als Zugabe.</p>
</div>
<div class="paragraph">
<p>Wir zeigen, wie Sie Doku genau wie Quellcode verwalten können, stakeholderspezifische Dokumente erzeugen und Diagramme automatisiert integrieren können.
Einige Teile dieser Doku können Sie sogar automatisiert testen.</p>
</div>
<div class="paragraph">
<p>Zwischendurch bekommen Sie zahlreiche Tipps, wie und wo Sie systematisch den Aufwand für Dokumentation reduzieren können und trotzdem lesbare, verständliche und praxistaugliche Ergebnisse produzieren.</p>
</div>
<div class="imageblock text-center">
<div class="content">
<a class="image" href="https://speakerdeck.com/rdmueller/dokumentation-am-riesen-beispiel-arc42-asciidoc-und-co-in-aktion"><img src="images/manual/speakerdeck1.png" alt="speakerdeck1"></a>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_gesunde_dokumentation_mit_asciidoctor">6.3.2. Gesunde Dokumentation mit AsciiDoctor</h4>
<div class="paragraph">
<p><a href="https://twitter.com/ahus1de">Alexander Schwartz</a></p>
</div>
<div class="paragraph">
<p>Autoren möchten Inhalte effizient dokumentieren und vorhandene Inhalte wiederverwenden.
Ein Leser möchte das Dokument in einem ansprechenden Layout präsentiert bekommen.</p>
</div>
<div class="paragraph">
<p>Das textbasierte AsciiDoc-Format bietet für einen Entwickler oder technischen Redakteur alle notwendigen Auszeichungselemente, um auch anspruchsvolle Dokumente zu schreiben.
So werden unter anderem Tabellen, Fußnoten und annotierte Quellcodes unterstützt.
Gleichzeitig ist es ähnlich leichtgewichtig wie z.B. das Markdown Format.
Für die Leser wird HTML, PDF oder EPUB generiert.</p>
</div>
<div class="paragraph">
<p>Da AsciiDoc wie Programmcode eingecheckt wird und Merge-Operationen einfach möglich sind, können Programmcode und Dokumentation zusammen versioniert und auf einem einheitlichen Stand gehalten werden.</p>
</div>
<div class="paragraph">
<p>Der Vortrag gibt eine kurze Einführung in AsciiDoc und die dazugehörigen Werkzeuge.</p>
</div>
<div class="imageblock text-center">
<div class="content">
<a class="image" href="https://speakerdeck.com/ahus1/gesunde-dokumentation-mit-asciidoctor"><img src="images/manual/speakerdeck2.png" alt="speakerdeck2"></a>
</div>
</div>
<div style="page-break-after: always;"></div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_acknowledgements_and_contributors">7. Acknowledgements and Contributors</h2>
<div class="sectionbody">
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/edit/master/src/docs/manual/05_contributors.adoc"><img src="https://img.shields.io/badge/improve-this%20doc-orange.svg" alt="improve this%20doc orange"></a>
</div>
</div>
<div class="imageblock right">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/issues/new?title=&body=%0A%0A%5BEnter%20feedback%20here%5D%0A%0A%0A---%0A%23page:manual/05_contributors.adoc"><img src="https://img.shields.io/badge/create-an%20issue-blue.svg" alt="create an%20issue blue"></a>
</div>
</div>
<div class="paragraph">
<p><span class="image gravatar"><img src="http://www.gravatar.com/avatar/cc5f3bf8b3cb91c985ed4fd046aa451d?d=identicon" alt="Ralf D. Mueller " width="32" title="Ralf D. Mueller "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/fa70f591d1ee3f8346afcf8c3973cac2?d=identicon" alt="Andreas Offenhaeuser " width="32" title="Andreas Offenhaeuser "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/eb5ceb4c4d2dfe351c58a8d005c6f3da?d=identicon" alt="Christoph Raaflaub " width="32" title="Christoph Raaflaub "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/adff7a288a49c60d485bb6f22739f3f2?d=identicon" alt="Daniel Bast " width="32" title="Daniel Bast "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/73252f762a6ec69bb59a23c8ff861c4a?d=identicon" alt="David M. Carr " width="32" title="David M. Carr "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/34ef1437fd31ff96c31dff58125e93fe?d=identicon" alt="Dr. Stefan Pfeiffer " width="32" title="Dr. Stefan Pfeiffer "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/64ec325b5e2d6c8b3d76e2df81056f2d?d=identicon" alt="Fabian Nonnenmacher " width="32" title="Fabian Nonnenmacher "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/69124393772349d32fb9fe975545757d?d=identicon" alt="Lars Francke " width="32" title="Lars Francke "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/aacd09e76c0affa36f76f8ef337a1536?d=identicon" alt="Luis Muniz " width="32" title="Luis Muniz "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/e499bc25cdf5bc253b89c3ced73035c4?d=identicon" alt="Maarten Gribnau " width="32" title="Maarten Gribnau "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/83c930e7c60a8e5b3201fb763cca1d6?d=identicon" alt="Michael Prieß " width="32" title="Michael Prieß "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/415bf2233be007b088ca81b749f02071?d=identicon" alt="Nils Mahlstaedt " width="32" title="Nils Mahlstaedt "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/a8ff23c0e396f621ed233027f21c27ea?d=identicon" alt="Peter Stange " width="32" title="Peter Stange "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/a00ae7656ba26679126c05ca950dd1fc?d=identicon" alt="Stefan Bohn " width="32" title="Stefan Bohn "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/9d3a17c1ac936fea4675ed193080c35b?d=identicon" alt="Werner Kevin " width="32" title="Werner Kevin "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/e7d9e6618a36e94b35d43cbd2925aee0?d=identicon" alt="bit-jkraushaar " width="32" title="bit-jkraushaar "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/fdbd46a8418be834d8ab743ca4b52455?d=identicon" alt="funkr " width="32" title="funkr "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/61c257b76a94d988f747a87850b7e114?d=identicon" alt="ghp-dev " width="32" title="ghp-dev "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/2e3af23bd872d2b971788a4519e3bf46?d=identicon" alt="jakubjab " width="32" title="jakubjab "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/be47969496f94a5a89159d70cd82bda0?d=identicon" alt="sabatmonk " width="32" title="sabatmonk "></span> <span class="image gravatar"><img src="http://www.gravatar.com/avatar/5107bc93f3e4d103ca06f4e68a64b38?d=identicon" alt="stehlih " width="32" title="stehlih "></span></p>
</div>
<div class="literalblock">
<div class="content">
<pre>1 minute to read</pre>
</div>
</div>
<div class="paragraph">
<p>This project is an open source project which is based on community efforts.</p>
</div>
<div class="paragraph">
<p>Many people are involved in the underlying technologies like AsciiDoc, AsciiDoctor, Gradle, arc42 etc.
This project depends and builds on them.</p>
</div>
<div class="paragraph">
<p>But it even more depends on the direct contributions made through giving feedback, creating issues, answering questions, or sending pull requests.</p>
</div>
<div class="paragraph">
<p>Here is an incomplete and unordered list of contributors:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/bodewig">Stefan Bodewig</a></p>
</li>
<li>
<p>MoePad</p>
</li>
<li>
<p><a href="https://github.com/opensource21">Niels</a></p>
</li>
<li>
<p><a href="https://github.com/wschaef">wschaef</a></p>
</li>
<li>
<p><a href="https://github.com/gernotstarke">Gernot Starke</a></p>
</li>
<li>
<p><a href="https://github.com/ahus1">Alexander Schwartz</a></p>
</li>
<li>
<p><a href="https://github.com/aheusingfeld">Alexander Heusingfeld</a></p>
</li>
<li>
<p><a href="https://github.com/mojavelinux">Dan Allen</a></p>
</li>
<li>
<p><a href="https://github.com/dl1ely">Stefan Pfeiffer</a></p>
</li>
<li>
<p><a href="https://github.com/isidorotrevino">isidorotrevino</a></p>
</li>
<li>
<p><a href="https://github.com/jakubjab">Jakub Jablonski</a></p>
</li>
<li>
<p><a href="https://github.com/FrankPohl">Frank Pohl</a></p>
</li>
<li>
<p><a href="https://twitter.com/ixchelruiz">Ixchel Ruiz</a></p>
</li>
<li>
<p><a href="https://twitter.com/ysb33r">Schalk Cronjé</a></p>
</li>
<li>
<p><a href="https://twitter.com/marioggar">Mario García</a></p>
</li>
<li>
<p><a href="https://github.com/joex2">Joe</a></p>
</li>
<li>
<p><a href="https://github.com/davidmc24">David M. Carr</a></p>
</li>
<li>
<p><a href="https://github.com/fnonnenmacher">Fabian Nonnenmacher</a></p>
</li>
<li>
<p><a href="https://twitter.com/stoeps">Christoph Stoettner</a></p>
</li>
<li>
<p><a href="https://github.com/funkr">Roman Funk</a></p>
</li>
<li>
<p><a href="https://github.com/ghp-dev">ghp-dev</a></p>
</li>
<li>
<p><a href="https://github.com/chrira">Christoph Raaflaub</a></p>
</li>
<li>
<p><a href="https://twitter.com/jagedn">Jorge Aguilera</a></p>
</li>
<li>
<p><a href="https://github.com/bohni">Stefan Bohn</a></p>
</li>
<li>
<p><a href="https://github.com/bit-jkraushaar">Jochen Kraushaar</a></p>
</li>
<li>
<p><a href="https://github.com/LuisMuniz">Luis Muniz</a></p>
</li>
<li>
<p><a href="https://anoff.io">Andreas Offenhaeuser</a></p>
</li>
<li>
<p><a href="https://github.com/dbast">Daniel Bast</a></p>
</li>
<li>
<p><a href="https://github.com/sabatmonk">Sabatmonk</a></p>
</li>
<li>
<p><a href="https://github.com/mindyou">Maarten Gribnau</a></p>
</li>
<li>
<p><a href="https://github.com/mpriess">Michael Prieß</a></p>
</li>
<li>
<p><a href="https://github.com/stehlih">Heiko Stehli</a></p>
</li>
<li>
<p><a href="https://github.com/peterstange">Peter Stange</a></p>
</li>
<li>
<p><a href="https://github.com/nilsmahlstaedt">Nils Mahlstädt</a> @ <a href="https://github.com/hmmh">hmmh</a></p>
</li>
<li>
<p><a href="https://github.com/k-werner">Kevin Werner</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>(please update your entry to match your preferences! :-)</p>
</div>
<div class="imageblock">
<div class="content">
<a class="image" href="https://github.com/docToolchain/docToolchain/graphs/contributors"><img src="https://img.shields.io/github/contributors/doctoolchain/doctoolchain.svg" alt="doctoolchain"></a>
</div>
</div>
<div style="page-break-after: always;"></div>
</div>
</div>
<div class="sect1">
<h2 id="_configuration_3">Appendix A: Configuration</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This appendix covers all configuration introduced by docToolchain.
AsciiDoc, AsciiDoctor, Gradle and other tools and libraries used know of more configuration settings and you can read about those in the corresponding documentation.</p>
</div>
<div class="sect2">
<h3 id="_mainconfigfile_and_docdir">A.1. <code>mainConfigFile</code> and <code>docDir</code></h3>
<div class="paragraph">
<p>docToolchain should be easy to use.
That&#8217;s why the goal is to have one config file with all settings for each project.
But first of all, docToolchain has to know where your documentation project is located.</p>
</div>
<div class="paragraph">
<p>If <code>docDir</code> is defined, the default for <code>mainConfigFile</code> is <code>Config.groovy</code> in the root folder of your <code>docDir</code>.</p>
</div>
<div class="paragraph">
<p>You have several options to specify the location of your documentation project (<code>docDir</code>) and the location of your config file (<code>mainConfigFile</code>).</p>
</div>
<div class="sect3">
<h4 id="_build_gradle">A.1.1. build.gradle</h4>
<div class="paragraph">
<p>If you use docToolchain as Gradle sub-project as described in <a href="https://docs-as-co.de/getstarted/tutorial2">our tutorial</a>, you will have some code in your main <code>build.gradle</code> which overwrites the location of the config file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy"><span class="comment">//configure docToolchain to use the main project's config</span>
project(<span class="string"><span class="delimiter">'</span><span class="content">docToolchain</span><span class="delimiter">'</span></span>) {
    <span class="keyword">if</span> (project.hasProperty(<span class="string"><span class="delimiter">'</span><span class="content">docDir</span><span class="delimiter">'</span></span>)) {
        docDir = <span class="string"><span class="delimiter">'</span><span class="content">../.</span><span class="delimiter">'</span></span>
        mainConfigFile = <span class="string"><span class="delimiter">'</span><span class="content">Config.groovy</span><span class="delimiter">'</span></span>
    } <span class="keyword">else</span> {
        println <span class="string"><span class="delimiter">&quot;</span><span class="content">=</span><span class="delimiter">&quot;</span></span>*<span class="integer">80</span>
        println <span class="string"><span class="delimiter">&quot;</span><span class="content">  please initialize the docToolchain submodule</span><span class="delimiter">&quot;</span></span>
        println <span class="string"><span class="delimiter">&quot;</span><span class="content">  by executing git submodule update -i</span><span class="delimiter">&quot;</span></span>
        println <span class="string"><span class="delimiter">&quot;</span><span class="content">=</span><span class="delimiter">&quot;</span></span>*<span class="integer">80</span>
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This way, you can place the config file wherever you like in your project.</p>
</div>
<div class="paragraph">
<p>Since the <code>mainConfigFile</code> is a standard Gradle-Property, you can also overwrite it through a command line option.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
it is not possible to overwrite properties of a sub project throught the <code>gradle.properties</code> of the main project.
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_commandline">A.1.2. Commandline</h4>
<div class="paragraph">
<p>Specify the property on the commandline</p>
</div>
<div class="literalblock">
<div class="content">
<pre>./gradlew generateHTML -PmainConfigFile=Config.groovy</pre>
</div>
</div>
<div class="paragraph">
<p>and if you use the doctoolchain script</p>
</div>
<div class="literalblock">
<div class="content">
<pre>doctoolchain &lt;docDir&gt; generateHTML -PmainConfigFile=Config.groovy</pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
you can verify the location of your <code>Config.groovy</code> by executing docToolchain with the <code>--info</code> parameter which sets the loglevel to <code>info</code>.
It will print the location on the command line (among other settings)
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_content_of_the_mainconfigfile">A.1.3. Content of the <code>mainConfigFile</code></h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">outputPath = <span class="string"><span class="delimiter">'</span><span class="content">build/docs</span><span class="delimiter">'</span></span>

<span class="comment">// Path where the docToolchain will search for the input files.</span>
<span class="comment">// This path is appended to the docDir property specified in gradle.properties</span>
<span class="comment">// or in the command line, and therefore must be relative to it.</span>

inputPath = <span class="string"><span class="delimiter">'</span><span class="content">src/docs</span><span class="delimiter">'</span></span>;


inputFiles = [
        [<span class="key">file</span>: <span class="string"><span class="delimiter">'</span><span class="content">manual.adoc</span><span class="delimiter">'</span></span>,       <span class="key">formats</span>: [<span class="string"><span class="delimiter">'</span><span class="content">html</span><span class="delimiter">'</span></span>,<span class="string"><span class="delimiter">'</span><span class="content">pdf</span><span class="delimiter">'</span></span>]],
]


taskInputsDirs = [<span class="string"><span class="delimiter">&quot;</span><span class="inline"><span class="inline-delimiter">${</span>inputPath<span class="inline-delimiter">}</span></span><span class="content">/images</span><span class="delimiter">&quot;</span></span>]

taskInputsFiles = <span class="type">[]</span>

<span class="comment">//*****************************************************************************************</span>

<span class="comment">//Configuration for exportChangelog</span>

exportChangelog = [:]

changelog.with {

    <span class="comment">// Directory of which the exportChangelog task will export the changelog.</span>
    <span class="comment">// It should be relative to the docDir directory provided in the</span>
    <span class="comment">// gradle.properties file.</span>
    dir = <span class="string"><span class="delimiter">'</span><span class="content">src/docs</span><span class="delimiter">'</span></span>

    <span class="comment">// Command used to fetch the list of changes.</span>
    <span class="comment">// It should be a single command taking a directory as a parameter.</span>
    <span class="comment">// You cannot use multiple commands with pipe between.</span>
    <span class="comment">// This command will be executed in the directory specified by changelogDir</span>
    <span class="comment">// it the environment inherited from the parent process.</span>
    <span class="comment">// This command should produce asciidoc text directly. The exportChangelog</span>
    <span class="comment">// task does not do any post-processing</span>
    <span class="comment">// of the output of that command.</span>
    <span class="comment">//</span>
    <span class="comment">// See also https://git-scm.com/docs/pretty-formats</span>
    cmd = <span class="string"><span class="delimiter">'</span><span class="content">git log --pretty=format:%x7c%x20%ad%x20%n%x7c%x20%an%x20%n%x7c%x20%s%x20%n --date=short</span><span class="delimiter">'</span></span>

}

<span class="comment">//*****************************************************************************************</span>

<span class="comment">//Configureation for publishToConfluence</span>

confluence = [:]

<span class="comment">// 'input' is an array of files to upload to Confluence with the ability</span>
<span class="comment">//          to configure a different parent page for each file.</span>
<span class="comment">//</span>
<span class="comment">// Attributes</span>
<span class="comment">// - 'file': absolute or relative path to the asciidoc generated html file to be exported</span>
<span class="comment">// - 'url': absolute URL to an asciidoc generated html file to be exported</span>
<span class="comment">// - 'ancestorId' (optional): the id of the parent page in Confluence as string; leave this empty</span>
<span class="comment">//                            if a new parent shall be created in the space</span>
<span class="comment">// - 'preambleTitle' (optional): the title of the page containing the preamble (everything</span>
<span class="comment">//                            before the first second level heading). Default is 'arc42'</span>
<span class="comment">//</span>
<span class="comment">// The following four keys can also be used in the global section below</span>
<span class="comment">// - 'spaceKey' (optional): page specific variable for the key of the confluence space to write to</span>
<span class="comment">// - 'createSubpages' (optional): page specific variable to determine whether &quot;.sect2&quot; sections shall be split from the current page into subpages</span>
<span class="comment">// - 'pagePrefix' (optional): page specific variable, the pagePrefix will be a prefix for the page title and it's sub-pages</span>
<span class="comment">//                            use this if you only have access to one confluence space but need to store several</span>
<span class="comment">//                            pages with the same title - a different pagePrefix will make them unique</span>
<span class="comment">// - 'pageSuffix' (optional): same usage as prefix but appended to the title and it's subpages</span>
<span class="comment">// only 'file' or 'url' is allowed. If both are given, 'url' is ignored</span>
confluence.with {
    input = [
            [ <span class="key">file</span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">build/docs/html5/arc42-template-de.html</span><span class="delimiter">&quot;</span></span> ],
    ]

    <span class="comment">// endpoint of the confluenceAPI (REST) to be used</span>
    api = <span class="string"><span class="delimiter">'</span><span class="content">https://[yourServer]/[context]/rest/api/</span><span class="delimiter">'</span></span>

<span class="comment">//    Additionally, spaceKey, createSubpages, pagePrefix and pageSuffix can be globally defined here. The assignment in the input array has precedence</span>

    <span class="comment">// the key of the confluence space to write to</span>
    spaceKey = <span class="string"><span class="delimiter">'</span><span class="content">asciidoc</span><span class="delimiter">'</span></span>

    <span class="comment">// the title of the page containing the preamble (everything the first second level heading). Default is 'arc42'</span>
    preambleTitle = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>

    <span class="comment">// variable to determine whether &quot;.sect2&quot; sections shall be split from the current page into subpages</span>
    createSubpages = <span class="predefined-constant">false</span>

    <span class="comment">// the pagePrefix will be a prefix for each page title</span>
    <span class="comment">// use this if you only have access to one confluence space but need to store several</span>
    <span class="comment">// pages with the same title - a different pagePrefix will make them unique</span>
    pagePrefix = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>

    pageSuffix = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>

    <span class="comment">// username:password of an account which has the right permissions to create and edit</span>
    <span class="comment">// confluence pages in the given space.</span>
    <span class="comment">// if you want to store it securely, fetch it from some external storage or leave it empty to fallback to gradle variables</span>
    <span class="comment">// set through gradle properties files or environment variables. The fallback uses the 'confluenceUser' and 'confluencePassword' keys.</span>
    <span class="comment">// you might even want to prompt the user for the password like in this example</span>

    credentials = <span class="string"><span class="delimiter">&quot;</span><span class="content">user:pass_or_token</span><span class="delimiter">&quot;</span></span>.bytes.encodeBase64().toString()

    <span class="comment">// HTML Content that will be included with every page published</span>
    <span class="comment">// directly after the TOC. If left empty no additional content will be</span>
    <span class="comment">// added</span>
    <span class="comment">// extraPageContent = '&lt;ac:structured-macro ac:name=&quot;warning&quot;&gt;&lt;ac:parameter ac:name=&quot;title&quot; /&gt;&lt;ac:rich-text-body&gt;This is a generated page, do not edit!&lt;/ac:rich-text-body&gt;&lt;/ac:structured-macro&gt;</span>
    extraPageContent = <span class="string"><span class="delimiter">'</span><span class="delimiter">'</span></span>

    <span class="comment">// enable or disable attachment uploads for local file references</span>
    enableAttachments = <span class="predefined-constant">false</span>

    <span class="comment">// default attachmentPrefix = attachment - All files to attach will require to be linked inside the document.</span>
    <span class="comment">// attachmentPrefix = &quot;attachment&quot;</span>


    <span class="comment">// Optional proxy configuration, only used to access Confluence</span>
    <span class="comment">// schema supports http and https</span>
    <span class="comment">// proxy = [host: 'my.proxy.com', port: 1234, schema: 'http']</span>
}
<span class="comment">//*****************************************************************************************</span>
<span class="comment">//Configuration for the export script 'exportEA.vbs'.</span>
<span class="comment">// The following parameters can be used to change the default behaviour of 'exportEA'.</span>
<span class="comment">// All parameter are optionally.</span>
<span class="comment">// Parameter 'connection' allows to select a certain database connection by using the ConnectionString as used for</span>
<span class="comment">// directly connecting to the project database instead of looking for EAP/EAPX files inside and below the 'src' folder.</span>
<span class="comment">// Parameter 'packageFilter' is an array of package GUID's to be used for export. All images inside and in all packages below the package represented by its GUID are exported.</span>
<span class="comment">// A packageGUID, that is not found in the currently opened project, is silently skipped.</span>
<span class="comment">// PackageGUID of multiple project files can be mixed in case multiple projects have to be opened.</span>

exportEA.with {
<span class="comment">// OPTIONAL: Set the connection to a certain project or comment it out to use all project files inside the src folder or its child folder.</span>
<span class="comment">// connection = &quot;DBType=1;Connect=Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=[THE_DB_NAME_OF_THE_PROJECT];Data Source=[server_hosting_database.com];LazyLoad=1;&quot;</span>
<span class="comment">// OPTIONAL: Add one or multiple packageGUIDs to be used for export. All packages are analysed, if no packageFilter is set.</span>
<span class="comment">// packageFilter = [</span>
<span class="comment">//                    &quot;{A237ECDE-5419-4d47-AECC-B836999E7AE0}&quot;,</span>
<span class="comment">//                    &quot;{B73FA2FB-267D-4bcd-3D37-5014AD8806D6}&quot;</span>
<span class="comment">//                  ]</span>
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_asciidoc_config">A.2. AsciiDoc config</h3>

</div>
<div class="sect2">
<h3 id="_command_line_parameters">A.3. Command Line Parameters</h3>

</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2020-06-09 07:10:22 UTC
</div>
</div>
</body>
</html>